diff options
author | Alexey Borzenkov <snaury@yandex-team.ru> | 2022-02-10 16:47:41 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:41 +0300 |
commit | 22d92781ba2a10b7fb5b977b7d1a5c40ff53885f (patch) | |
tree | 852611fd27f734847435b37aa5b0ad5d8b1c10ac /library/cpp | |
parent | 667a4ee7da2e004784b9c3cfab824a81e96f4d66 (diff) | |
download | ydb-22d92781ba2a10b7fb5b977b7d1a5c40ff53885f.tar.gz |
Restoring authorship annotation for Alexey Borzenkov <snaury@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'library/cpp')
70 files changed, 4529 insertions, 4529 deletions
diff --git a/library/cpp/actors/core/actor.cpp b/library/cpp/actors/core/actor.cpp index 6f9ba6a42b..638bfb72fa 100644 --- a/library/cpp/actors/core/actor.cpp +++ b/library/cpp/actors/core/actor.cpp @@ -37,10 +37,10 @@ namespace NActors { TlsActivationContext->ExecutorThread.Schedule(deadline, ev, cookie); } - void TActivationContext::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) { - TlsActivationContext->ExecutorThread.Schedule(deadline, ev, cookie); - } - + void TActivationContext::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) { + TlsActivationContext->ExecutorThread.Schedule(deadline, ev, cookie); + } + void TActivationContext::Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) { TlsActivationContext->ExecutorThread.Schedule(delta, ev, cookie); } @@ -87,14 +87,14 @@ namespace NActors { return TlsActivationContext->ExecutorThread.ActorSystem; } - i64 TActivationContext::GetCurrentEventTicks() { + i64 TActivationContext::GetCurrentEventTicks() { return GetCycleCountFast() - TlsActivationContext->EventStart; - } - - double TActivationContext::GetCurrentEventTicksAsSeconds() { - return NHPTimer::GetSeconds(GetCurrentEventTicks()); - } - + } + + double TActivationContext::GetCurrentEventTicksAsSeconds() { + return NHPTimer::GetSeconds(GetCurrentEventTicks()); + } + TActorId TActorContext::Register(IActor* actor, TMailboxType::EType mailboxType, ui32 poolId) const { return ExecutorThread.RegisterActor(actor, mailboxType, poolId, SelfID); } @@ -107,10 +107,10 @@ namespace NActors { ExecutorThread.Schedule(deadline, new IEventHandle(SelfID, TActorId(), ev), cookie); } - void TActorContext::Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie) const { - ExecutorThread.Schedule(deadline, new IEventHandle(SelfID, TActorId(), ev), cookie); - } - + void TActorContext::Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie) const { + ExecutorThread.Schedule(deadline, new IEventHandle(SelfID, TActorId(), ev), cookie); + } + void TActorContext::Schedule(TDuration delta, IEventBase* ev, ISchedulerCookie* cookie) const { ExecutorThread.Schedule(delta, new IEventHandle(SelfID, TActorId(), ev), cookie); } @@ -119,10 +119,10 @@ namespace NActors { TlsActivationContext->ExecutorThread.Schedule(deadline, new IEventHandle(SelfActorId, TActorId(), ev), cookie); } - void IActor::Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie) const noexcept { - TlsActivationContext->ExecutorThread.Schedule(deadline, new IEventHandle(SelfActorId, TActorId(), ev), cookie); - } - + void IActor::Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie) const noexcept { + TlsActivationContext->ExecutorThread.Schedule(deadline, new IEventHandle(SelfActorId, TActorId(), ev), cookie); + } + void IActor::Schedule(TDuration delta, IEventBase* ev, ISchedulerCookie* cookie) const noexcept { TlsActivationContext->ExecutorThread.Schedule(delta, new IEventHandle(SelfActorId, TActorId(), ev), cookie); } @@ -131,10 +131,10 @@ namespace NActors { return TlsActivationContext->ExecutorThread.ActorSystem->Timestamp(); } - TMonotonic TActivationContext::Monotonic() { - return TlsActivationContext->ExecutorThread.ActorSystem->Monotonic(); - } - + TMonotonic TActivationContext::Monotonic() { + return TlsActivationContext->ExecutorThread.ActorSystem->Monotonic(); + } + TInstant TActorContext::Now() const { return ExecutorThread.ActorSystem->Timestamp(); } diff --git a/library/cpp/actors/core/actor.h b/library/cpp/actors/core/actor.h index ed29bd14b9..9c167ab595 100644 --- a/library/cpp/actors/core/actor.h +++ b/library/cpp/actors/core/actor.h @@ -1,7 +1,7 @@ #pragma once #include "event.h" -#include "monotonic.h" +#include "monotonic.h" #include <util/system/tls.h> #include <library/cpp/actors/util/local_process_key.h> @@ -24,13 +24,13 @@ namespace NActors { public: TMailboxHeader& Mailbox; TExecutorThread& ExecutorThread; - const NHPTimer::STime EventStart; + const NHPTimer::STime EventStart; protected: - explicit TActivationContext(TMailboxHeader& mailbox, TExecutorThread& executorThread, NHPTimer::STime eventStart) + explicit TActivationContext(TMailboxHeader& mailbox, TExecutorThread& executorThread, NHPTimer::STime eventStart) : Mailbox(mailbox) , ExecutorThread(executorThread) - , EventStart(eventStart) + , EventStart(eventStart) { } @@ -40,22 +40,22 @@ namespace NActors { /** * Schedule one-shot event that will be send at given time point in the future. * - * @param deadline the wallclock time point in future when event must be send + * @param deadline the wallclock time point in future when event must be send * @param ev the event to send * @param cookie cookie that will be piggybacked with event */ static void Schedule(TInstant deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); /** - * Schedule one-shot event that will be send at given time point in the future. - * - * @param deadline the monotonic time point in future when event must be send - * @param ev the event to send - * @param cookie cookie that will be piggybacked with event - */ - static void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); - - /** + * Schedule one-shot event that will be send at given time point in the future. + * + * @param deadline the monotonic time point in future when event must be send + * @param ev the event to send + * @param cookie cookie that will be piggybacked with event + */ + static void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); + + /** * Schedule one-shot event that will be send after given delay. * * @param delta the time from now to delay event sending @@ -65,7 +65,7 @@ namespace NActors { static void Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); static TInstant Now(); - static TMonotonic Monotonic(); + static TMonotonic Monotonic(); NLog::TSettings* LoggerSettings() const; // register new actor in ActorSystem on new fresh mailbox. @@ -83,16 +83,16 @@ namespace NActors { static TActorId InterconnectProxy(ui32 nodeid); static TActorSystem* ActorSystem(); - - static i64 GetCurrentEventTicks(); - static double GetCurrentEventTicksAsSeconds(); + + static i64 GetCurrentEventTicks(); + static double GetCurrentEventTicksAsSeconds(); }; struct TActorContext: public TActivationContext { const TActorId SelfID; explicit TActorContext(TMailboxHeader& mailbox, TExecutorThread& executorThread, NHPTimer::STime eventStart, const TActorId& selfID) - : TActivationContext(mailbox, executorThread, eventStart) + : TActivationContext(mailbox, executorThread, eventStart) , SelfID(selfID) { } @@ -110,22 +110,22 @@ namespace NActors { /** * Schedule one-shot event that will be send at given time point in the future. * - * @param deadline the wallclock time point in future when event must be send + * @param deadline the wallclock time point in future when event must be send * @param ev the event to send * @param cookie cookie that will be piggybacked with event */ void Schedule(TInstant deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const; /** - * Schedule one-shot event that will be send at given time point in the future. - * - * @param deadline the monotonic time point in future when event must be send - * @param ev the event to send - * @param cookie cookie that will be piggybacked with event - */ - void Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const; - - /** + * Schedule one-shot event that will be send at given time point in the future. + * + * @param deadline the monotonic time point in future when event must be send + * @param ev the event to send + * @param cookie cookie that will be piggybacked with event + */ + void Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const; + + /** * Schedule one-shot event that will be send after given delay. * * @param delta the time from now to delay event sending @@ -135,7 +135,7 @@ namespace NActors { void Schedule(TDuration delta, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const; TActorContext MakeFor(const TActorId& otherId) const { - return TActorContext(Mailbox, ExecutorThread, EventStart, otherId); + return TActorContext(Mailbox, ExecutorThread, EventStart, otherId); } // register new actor in ActorSystem on new fresh mailbox. @@ -179,22 +179,22 @@ namespace NActors { /** * Schedule one-shot event that will be send at given time point in the future. * - * @param deadline the wallclock time point in future when event must be send + * @param deadline the wallclock time point in future when event must be send * @param ev the event to send * @param cookie cookie that will be piggybacked with event */ virtual void Schedule(TInstant deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept = 0; /** - * Schedule one-shot event that will be send at given time point in the future. - * - * @param deadline the monotonic time point in future when event must be send - * @param ev the event to send - * @param cookie cookie that will be piggybacked with event - */ - virtual void Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept = 0; - - /** + * Schedule one-shot event that will be send at given time point in the future. + * + * @param deadline the monotonic time point in future when event must be send + * @param ev the event to send + * @param cookie cookie that will be piggybacked with event + */ + virtual void Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept = 0; + + /** * Schedule one-shot event that will be send after given delay. * * @param delta the time from now to delay event sending @@ -240,11 +240,11 @@ namespace NActors { INTERCONNECT_SESSION_KILLER = 286, ACTOR_SYSTEM_SCHEDULER_ACTOR = 312, ACTOR_FUTURE_CALLBACK = 337, - INTERCONNECT_MONACTOR = 362, + INTERCONNECT_MONACTOR = 362, INTERCONNECT_LOAD_ACTOR = 376, INTERCONNECT_LOAD_RESPONDER = 377, NAMESERVICE = 450, - DNS_RESOLVER = 481, + DNS_RESOLVER = 481, INTERCONNECT_PROXY_WRAPPER = 546, }; @@ -362,7 +362,7 @@ namespace NActors { } void Schedule(TInstant deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept final; - void Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept final; + void Schedule(TMonotonic deadline, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept final; void Schedule(TDuration delta, IEventBase* ev, ISchedulerCookie* cookie = nullptr) const noexcept final; // register new actor in ActorSystem on new fresh mailbox. @@ -456,7 +456,7 @@ namespace NActors { inline TActorContext TActivationContext::ActorContextFor(TActorId id) { auto& tls = *TlsActivationContext; - return TActorContext(tls.Mailbox, tls.ExecutorThread, tls.EventStart, id); + return TActorContext(tls.Mailbox, tls.ExecutorThread, tls.EventStart, id); } class TDecorator : public IActor { diff --git a/library/cpp/actors/core/actor_coroutine.cpp b/library/cpp/actors/core/actor_coroutine.cpp index 0ab4d2b24d..d3bcdafbd3 100644 --- a/library/cpp/actors/core/actor_coroutine.cpp +++ b/library/cpp/actors/core/actor_coroutine.cpp @@ -1,9 +1,9 @@ #include "actor_coroutine.h" #include "executor_thread.h" -#include <util/system/sanitizers.h> +#include <util/system/sanitizers.h> #include <util/system/type_name.h> - + namespace NActors { static constexpr size_t StackOverflowGap = 4096; static char GoodStack[StackOverflowGap]; @@ -92,8 +92,8 @@ namespace NActors { } // prepare actor context for in-coroutine use - TActivationContext *ac = TlsActivationContext; - TlsActivationContext = nullptr; + TActivationContext *ac = TlsActivationContext; + TlsActivationContext = nullptr; TActorContext ctx(ac->Mailbox, ac->ExecutorThread, ac->EventStart, SelfActorId); ActorContext = &ctx; diff --git a/library/cpp/actors/core/actorid.h b/library/cpp/actors/core/actorid.h index d972b1a0ff..c9e6173173 100644 --- a/library/cpp/actors/core/actorid.h +++ b/library/cpp/actors/core/actorid.h @@ -12,13 +12,13 @@ namespace NActors { // next 20 bits - node id itself struct TActorId { - static constexpr ui32 MaxServiceIDLength = 12; - static constexpr ui32 MaxPoolID = 0x000007FF; - static constexpr ui32 MaxNodeId = 0x000FFFFF; - static constexpr ui32 PoolIndexShift = 20; - static constexpr ui32 PoolIndexMask = MaxPoolID << PoolIndexShift; - static constexpr ui32 ServiceMask = 0x80000000; - static constexpr ui32 NodeIdMask = MaxNodeId; + static constexpr ui32 MaxServiceIDLength = 12; + static constexpr ui32 MaxPoolID = 0x000007FF; + static constexpr ui32 MaxNodeId = 0x000FFFFF; + static constexpr ui32 PoolIndexShift = 20; + static constexpr ui32 PoolIndexMask = MaxPoolID << PoolIndexShift; + static constexpr ui32 ServiceMask = 0x80000000; + static constexpr ui32 NodeIdMask = MaxNodeId; private: union { diff --git a/library/cpp/actors/core/actorsystem.cpp b/library/cpp/actors/core/actorsystem.cpp index c58698a206..4130b0d4da 100644 --- a/library/cpp/actors/core/actorsystem.cpp +++ b/library/cpp/actors/core/actorsystem.cpp @@ -43,7 +43,7 @@ namespace NActors { , Scheduler(setup->Scheduler) , InterconnectCount((ui32)setup->Interconnect.ProxyActors.size()) , CurrentTimestamp(0) - , CurrentMonotonic(0) + , CurrentMonotonic(0) , CurrentIDCounter(RandomNumber<ui64>()) , SystemSetup(setup.Release()) , DefSelfID(NodeId, "actorsystem") @@ -69,15 +69,15 @@ namespace NActors { #endif TActorId recipient = ev->GetRecipientRewrite(); - const ui32 recpNodeId = recipient.NodeId(); + const ui32 recpNodeId = recipient.NodeId(); if (recpNodeId != NodeId && recpNodeId != 0) { // if recipient is not local one - rewrite with forward instruction Y_VERIFY_DEBUG(!ev->HasEvent() || ev->GetBase()->IsSerializable()); - Y_VERIFY(ev->Recipient == recipient, - "Event rewrite from %s to %s would be lost via interconnect", - ev->Recipient.ToString().c_str(), - recipient.ToString().c_str()); + Y_VERIFY(ev->Recipient == recipient, + "Event rewrite from %s to %s would be lost via interconnect", + ev->Recipient.ToString().c_str(), + recipient.ToString().c_str()); recipient = InterconnectProxy(recpNodeId); ev->Rewrite(TEvInterconnect::EvForward, recipient); } @@ -119,20 +119,20 @@ namespace NActors { } void TActorSystem::Schedule(TInstant deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) const { - Schedule(deadline - Timestamp(), ev, cookie); - } - - void TActorSystem::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) const { - const auto current = Monotonic(); - if (deadline < current) - deadline = current; - - TTicketLock::TGuard guard(&ScheduleLock); - ScheduleQueue->Writer.Push(deadline.MicroSeconds(), ev.Release(), cookie); + Schedule(deadline - Timestamp(), ev, cookie); } + void TActorSystem::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) const { + const auto current = Monotonic(); + if (deadline < current) + deadline = current; + + TTicketLock::TGuard guard(&ScheduleLock); + ScheduleQueue->Writer.Push(deadline.MicroSeconds(), ev.Release(), cookie); + } + void TActorSystem::Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) const { - const auto deadline = Monotonic() + delta; + const auto deadline = Monotonic() + delta; TTicketLock::TGuard guard(&ScheduleLock); ScheduleQueue->Writer.Push(deadline.MicroSeconds(), ev.Release(), cookie); @@ -211,7 +211,7 @@ namespace NActors { TVector<NSchedulerQueue::TReader*> scheduleReaders; scheduleReaders.push_back(&ScheduleQueue->Reader); CpuManager->PrepareStart(scheduleReaders, this); - Scheduler->Prepare(this, &CurrentTimestamp, &CurrentMonotonic); + Scheduler->Prepare(this, &CurrentTimestamp, &CurrentMonotonic); Scheduler->PrepareSchedules(&scheduleReaders.front(), (ui32)scheduleReaders.size()); // setup interconnect proxies @@ -242,9 +242,9 @@ namespace NActors { // ok, setup complete, we could destroy setup config SystemSetup.Destroy(); - Scheduler->PrepareStart(); + Scheduler->PrepareStart(); CpuManager->Start(); - Send(MakeSchedulerActorId(), new TEvSchedulerInitialize(scheduleReaders, &CurrentTimestamp, &CurrentMonotonic)); + Send(MakeSchedulerActorId(), new TEvSchedulerInitialize(scheduleReaders, &CurrentTimestamp, &CurrentMonotonic)); Scheduler->Start(); } diff --git a/library/cpp/actors/core/actorsystem.h b/library/cpp/actors/core/actorsystem.h index 40499d7586..80d33901b0 100644 --- a/library/cpp/actors/core/actorsystem.h +++ b/library/cpp/actors/core/actorsystem.h @@ -27,7 +27,7 @@ namespace NActors { char data[12]; memcpy(data, "ICProxy@", 8); memcpy(data + 8, &destNodeId, sizeof(ui32)); - return TActorId(0, TStringBuf(data, 12)); + return TActorId(0, TStringBuf(data, 12)); } inline bool IsInterconnectProxyId(const TActorId& actorId) { @@ -69,7 +69,7 @@ namespace NActors { /** * Schedule one-shot event that will be send at given time point in the future. * - * @param deadline the wallclock time point in future when event must be send + * @param deadline the wallclock time point in future when event must be send * @param ev the event to send * @param cookie cookie that will be piggybacked with event * @param workerId index of thread which will perform event dispatching @@ -77,16 +77,16 @@ namespace NActors { virtual void Schedule(TInstant deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) = 0; /** - * Schedule one-shot event that will be send at given time point in the future. - * - * @param deadline the monotonic time point in future when event must be send - * @param ev the event to send - * @param cookie cookie that will be piggybacked with event + * Schedule one-shot event that will be send at given time point in the future. + * + * @param deadline the monotonic time point in future when event must be send + * @param ev the event to send + * @param cookie cookie that will be piggybacked with event * @param workerId index of thread which will perform event dispatching - */ + */ virtual void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) = 0; - - /** + + /** * Schedule one-shot event that will be send after given delay. * * @param delta the time from now to delay event sending @@ -136,9 +136,9 @@ namespace NActors { virtual ~ISchedulerThread() { } - virtual void Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) = 0; + virtual void Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) = 0; virtual void PrepareSchedules(NSchedulerQueue::TReader** readers, ui32 scheduleReadersCount) = 0; - virtual void PrepareStart() { /* empty */ } + virtual void PrepareStart() { /* empty */ } virtual void Start() = 0; virtual void PrepareStop() = 0; virtual void Stop() = 0; @@ -226,7 +226,7 @@ namespace NActors { TArrayHolder<TActorId> Interconnect; volatile ui64 CurrentTimestamp; - volatile ui64 CurrentMonotonic; + volatile ui64 CurrentMonotonic; volatile ui64 CurrentIDCounter; THolder<NSchedulerQueue::TQueueType> ScheduleQueue; @@ -264,22 +264,22 @@ namespace NActors { /** * Schedule one-shot event that will be send at given time point in the future. * - * @param deadline the wallclock time point in future when event must be send + * @param deadline the wallclock time point in future when event must be send * @param ev the event to send * @param cookie cookie that will be piggybacked with event */ void Schedule(TInstant deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr) const; /** - * Schedule one-shot event that will be send at given time point in the future. - * - * @param deadline the monotonic time point in future when event must be send - * @param ev the event to send - * @param cookie cookie that will be piggybacked with event - */ - void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr) const; - - /** + * Schedule one-shot event that will be send at given time point in the future. + * + * @param deadline the monotonic time point in future when event must be send + * @param ev the event to send + * @param cookie cookie that will be piggybacked with event + */ + void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr) const; + + /** * Schedule one-shot event that will be send after given delay. * * @param delta the time from now to delay event sending @@ -340,10 +340,10 @@ namespace NActors { return TInstant::MicroSeconds(RelaxedLoad(&CurrentTimestamp)); } - TMonotonic Monotonic() const { - return TMonotonic::MicroSeconds(RelaxedLoad(&CurrentMonotonic)); - } - + TMonotonic Monotonic() const { + return TMonotonic::MicroSeconds(RelaxedLoad(&CurrentMonotonic)); + } + template <typename T> T* AppData() const { return (T*)AppData0; diff --git a/library/cpp/actors/core/events.h b/library/cpp/actors/core/events.h index 702cf50fad..381dac03af 100644 --- a/library/cpp/actors/core/events.h +++ b/library/cpp/actors/core/events.h @@ -4,7 +4,7 @@ #include "event_pb.h" #include <library/cpp/actors/protos/actors.pb.h> -#include <util/system/unaligned_mem.h> +#include <util/system/unaligned_mem.h> namespace NActors { struct TEvents { @@ -161,9 +161,9 @@ namespace NActors { private: static TString MakeData(ui32 sourceType, ui32 reason) { TString s = TString::Uninitialized(sizeof(ui32) + sizeof(ui32)); - char *p = s.Detach(); - WriteUnaligned<ui32>(p + 0, sourceType); - WriteUnaligned<ui32>(p + 4, reason); + char *p = s.Detach(); + WriteUnaligned<ui32>(p + 0, sourceType); + WriteUnaligned<ui32>(p + 4, reason); return s; } }; diff --git a/library/cpp/actors/core/events_undelivered.cpp b/library/cpp/actors/core/events_undelivered.cpp index 23deaffd10..3b96625cab 100644 --- a/library/cpp/actors/core/events_undelivered.cpp +++ b/library/cpp/actors/core/events_undelivered.cpp @@ -32,9 +32,9 @@ namespace NActors { IEventBase* TEvents::TEvUndelivered::Load(TEventSerializedData* bufs) { TString str = bufs->GetString(); Y_VERIFY(str.size() == (sizeof(ui32) + sizeof(ui32))); - const char* p = str.data(); - const ui64 sourceType = ReadUnaligned<ui32>(p + 0); - const ui64 reason = ReadUnaligned<ui32>(p + 4); + const char* p = str.data(); + const ui64 sourceType = ReadUnaligned<ui32>(p + 0); + const ui64 reason = ReadUnaligned<ui32>(p + 4); return new TEvUndelivered(sourceType, reason); } diff --git a/library/cpp/actors/core/executor_pool_basic.cpp b/library/cpp/actors/core/executor_pool_basic.cpp index 4dce16939a..87315a6a6a 100644 --- a/library/cpp/actors/core/executor_pool_basic.cpp +++ b/library/cpp/actors/core/executor_pool_basic.cpp @@ -87,17 +87,17 @@ namespace NActors { if (x < 0) { #if defined ACTORSLIB_COLLECT_EXEC_STATS if (AtomicGetAndIncrement(ThreadUtilization) == 0) { - // Initially counter contains -t0, the pool start timestamp - // When the first thread goes to sleep we add t1, so the counter - // becomes t1-t0 >= 0, or the duration of max utilization so far. - // If the counter was negative and becomes positive, that means - // counter just turned into a duration and we should store that - // duration. Otherwise another thread raced with us and - // subtracted some other timestamp t2. + // Initially counter contains -t0, the pool start timestamp + // When the first thread goes to sleep we add t1, so the counter + // becomes t1-t0 >= 0, or the duration of max utilization so far. + // If the counter was negative and becomes positive, that means + // counter just turned into a duration and we should store that + // duration. Otherwise another thread raced with us and + // subtracted some other timestamp t2. const i64 t = GetCycleCountFast(); - const i64 x = AtomicGetAndAdd(MaxUtilizationCounter, t); - if (x < 0 && x + t > 0) - AtomicStore(&MaxUtilizationAccumulator, x + t); + const i64 x = AtomicGetAndAdd(MaxUtilizationCounter, t); + if (x < 0 && x + t > 0) + AtomicStore(&MaxUtilizationAccumulator, x + t); } #endif @@ -126,7 +126,7 @@ namespace NActors { if (!doSpin) { break; } - if (RelaxedLoad(&StopFlag)) { + if (RelaxedLoad(&StopFlag)) { break; } } @@ -159,20 +159,20 @@ namespace NActors { #if defined ACTORSLIB_COLLECT_EXEC_STATS if (AtomicDecrement(ThreadUtilization) == 0) { - // When we started sleeping counter contained t1-t0, or the - // last duration of max utilization. Now we subtract t2 >= t1, - // which turns counter negative again, and the next sleep cycle - // at timestamp t3 would be adding some new duration t3-t2. - // If the counter was positive and becomes negative that means - // there are no current races with other threads and we should - // store the last positive duration we observed. Multiple - // threads may be adding and subtracting values in potentially - // arbitrary order, which would cause counter to oscillate - // around zero. When it crosses zero is a good indication of a - // correct value. + // When we started sleeping counter contained t1-t0, or the + // last duration of max utilization. Now we subtract t2 >= t1, + // which turns counter negative again, and the next sleep cycle + // at timestamp t3 would be adding some new duration t3-t2. + // If the counter was positive and becomes negative that means + // there are no current races with other threads and we should + // store the last positive duration we observed. Multiple + // threads may be adding and subtracting values in potentially + // arbitrary order, which would cause counter to oscillate + // around zero. When it crosses zero is a good indication of a + // correct value. const i64 t = GetCycleCountFast(); - const i64 x = AtomicGetAndAdd(MaxUtilizationCounter, -t); - if (x > 0 && x - t < 0) + const i64 x = AtomicGetAndAdd(MaxUtilizationCounter, -t); + if (x > 0 && x - t < 0) AtomicStore(&MaxUtilizationAccumulator, x); } #endif @@ -305,18 +305,18 @@ namespace NActors { void TBasicExecutorPool::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) { Y_VERIFY_DEBUG(workerId < PoolThreads); - - const auto current = ActorSystem->Monotonic(); - if (deadline < current) - deadline = current; - + + const auto current = ActorSystem->Monotonic(); + if (deadline < current) + deadline = current; + ScheduleWriters[workerId].Push(deadline.MicroSeconds(), ev.Release(), cookie); - } - + } + void TBasicExecutorPool::Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) { Y_VERIFY_DEBUG(workerId < PoolThreads); - const auto deadline = ActorSystem->Monotonic() + delta; + const auto deadline = ActorSystem->Monotonic() + delta; ScheduleWriters[workerId].Push(deadline.MicroSeconds(), ev.Release(), cookie); } diff --git a/library/cpp/actors/core/executor_pool_basic_ut.cpp b/library/cpp/actors/core/executor_pool_basic_ut.cpp index 76dff693af..b3d6d8b329 100644 --- a/library/cpp/actors/core/executor_pool_basic_ut.cpp +++ b/library/cpp/actors/core/executor_pool_basic_ut.cpp @@ -47,8 +47,8 @@ public: if (GetCounter() == 0) { break; } - - Sleep(TDuration::MilliSeconds(1)); + + Sleep(TDuration::MilliSeconds(1)); } } @@ -69,8 +69,8 @@ private: { Y_UNUSED(ev); Action(); - TAtomicBase count = AtomicDecrement(Counter); - Y_VERIFY(count != Max<TAtomicBase>()); + TAtomicBase count = AtomicDecrement(Counter); + Y_VERIFY(count != Max<TAtomicBase>()); if (count) { Send(Receiver, new TEvMsg()); } @@ -206,19 +206,19 @@ Y_UNIT_TEST_SUITE(BasicExecutorPool) { actorSystem.Send(changerActorId, new TEvMsg()); while (true) { - size_t maxCounter = 0; + size_t maxCounter = 0; for (size_t i = 0; i < size; ++i) { - maxCounter = Max(maxCounter, actors[i]->GetCounter()); - } - - if (maxCounter == 0) { - break; + maxCounter = Max(maxCounter, actors[i]->GetCounter()); } + if (maxCounter == 0) { + break; + } + auto now = TInstant::Now(); - UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); + UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); - Sleep(TDuration::MilliSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); } changerActor->Stop(); @@ -242,9 +242,9 @@ Y_UNIT_TEST_SUITE(BasicExecutorPool) { while (actor->GetCounter()) { auto now = TInstant::Now(); - UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Counter is " << actor->GetCounter()); - - Sleep(TDuration::MilliSeconds(1)); + UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Counter is " << actor->GetCounter()); + + Sleep(TDuration::MilliSeconds(1)); } } @@ -275,19 +275,19 @@ Y_UNIT_TEST_SUITE(BasicExecutorPool) { while (true) { - size_t maxCounter = 0; + size_t maxCounter = 0; for (size_t i = 0; i < size; ++i) { - maxCounter = Max(maxCounter, actors[i]->GetCounter()); - } - - if (maxCounter == 0) { - break; + maxCounter = Max(maxCounter, actors[i]->GetCounter()); } + if (maxCounter == 0) { + break; + } + auto now = TInstant::Now(); - UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); + UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); - Sleep(TDuration::MilliSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); } } @@ -319,19 +319,19 @@ Y_UNIT_TEST_SUITE(BasicExecutorPool) { while (true) { - size_t maxCounter = 0; + size_t maxCounter = 0; for (size_t i = 0; i < actorsCount; ++i) { - maxCounter = Max(maxCounter, actors[i]->GetCounter()); - } - - if (maxCounter == 0) { - break; + maxCounter = Max(maxCounter, actors[i]->GetCounter()); } + if (maxCounter == 0) { + break; + } + auto now = TInstant::Now(); - UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); + UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); - Sleep(TDuration::MilliSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); } } @@ -362,19 +362,19 @@ Y_UNIT_TEST_SUITE(BasicExecutorPool) { } while (true) { - size_t maxCounter = 0; + size_t maxCounter = 0; for (size_t i = 0; i < actorsCount; ++i) { - maxCounter = Max(maxCounter, actors[i]->GetCounter()); - } - - if (maxCounter == 0) { - break; + maxCounter = Max(maxCounter, actors[i]->GetCounter()); } + if (maxCounter == 0) { + break; + } + auto now = TInstant::Now(); - UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); + UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Max counter is " << maxCounter); - Sleep(TDuration::MilliSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); } } diff --git a/library/cpp/actors/core/executor_pool_io.cpp b/library/cpp/actors/core/executor_pool_io.cpp index fb557ae6b0..f4f13c9c20 100644 --- a/library/cpp/actors/core/executor_pool_io.cpp +++ b/library/cpp/actors/core/executor_pool_io.cpp @@ -30,33 +30,33 @@ namespace NActors { ui32 workerId = wctx.WorkerId; Y_VERIFY_DEBUG(workerId < PoolThreads); - NHPTimer::STime elapsed = 0; - NHPTimer::STime parked = 0; + NHPTimer::STime elapsed = 0; + NHPTimer::STime parked = 0; NHPTimer::STime hpstart = GetCycleCountFast(); - NHPTimer::STime hpnow; - + NHPTimer::STime hpnow; + const TAtomic x = AtomicDecrement(Semaphore); if (x < 0) { TThreadCtx& threadCtx = Threads[workerId]; ThreadQueue.Push(workerId + 1, revolvingCounter); hpnow = GetCycleCountFast(); - elapsed += hpnow - hpstart; + elapsed += hpnow - hpstart; if (threadCtx.Pad.Park()) return 0; hpstart = GetCycleCountFast(); - parked += hpstart - hpnow; + parked += hpstart - hpnow; } while (!RelaxedLoad(&StopFlag)) { - if (const ui32 activation = Activations.Pop(++revolvingCounter)) { + if (const ui32 activation = Activations.Pop(++revolvingCounter)) { hpnow = GetCycleCountFast(); - elapsed += hpnow - hpstart; + elapsed += hpnow - hpstart; wctx.AddElapsedCycles(IActor::ACTOR_SYSTEM, elapsed); - if (parked > 0) { + if (parked > 0) { wctx.AddParkedCycles(parked); - } + } return activation; - } + } SpinLockPause(); } @@ -69,18 +69,18 @@ namespace NActors { void TIOExecutorPool::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) { Y_UNUSED(workerId); - - const auto current = ActorSystem->Monotonic(); - if (deadline < current) - deadline = current; - - TTicketLock::TGuard guard(&ScheduleLock); - ScheduleQueue->Writer.Push(deadline.MicroSeconds(), ev.Release(), cookie); - } - + + const auto current = ActorSystem->Monotonic(); + if (deadline < current) + deadline = current; + + TTicketLock::TGuard guard(&ScheduleLock); + ScheduleQueue->Writer.Push(deadline.MicroSeconds(), ev.Release(), cookie); + } + void TIOExecutorPool::Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie, TWorkerId workerId) { Y_UNUSED(workerId); - const auto deadline = ActorSystem->Monotonic() + delta; + const auto deadline = ActorSystem->Monotonic() + delta; TTicketLock::TGuard guard(&ScheduleLock); ScheduleQueue->Writer.Push(deadline.MicroSeconds(), ev.Release(), cookie); diff --git a/library/cpp/actors/core/executor_pool_io.h b/library/cpp/actors/core/executor_pool_io.h index e576d642a1..7de6fd0528 100644 --- a/library/cpp/actors/core/executor_pool_io.h +++ b/library/cpp/actors/core/executor_pool_io.h @@ -17,7 +17,7 @@ namespace NActors { }; TArrayHolder<TThreadCtx> Threads; - TUnorderedCache<ui32, 512, 4> ThreadQueue; + TUnorderedCache<ui32, 512, 4> ThreadQueue; THolder<NSchedulerQueue::TQueueType> ScheduleQueue; TTicketLock ScheduleLock; diff --git a/library/cpp/actors/core/executor_pool_united.cpp b/library/cpp/actors/core/executor_pool_united.cpp index dac6245635..4ae9ec4c73 100644 --- a/library/cpp/actors/core/executor_pool_united.cpp +++ b/library/cpp/actors/core/executor_pool_united.cpp @@ -1235,7 +1235,7 @@ namespace NActors { inline void TUnitedWorkers::TryWake(TPoolId pool) { // Avoid using multiple atomic seq_cst loads in cycle, use barrier once AtomicBarrier(); - + // Scan every allowed cpu in pool's wakeup order and try to wake the first idle cpu if (RelaxedLoad(&Pools[pool].Waiters) > 0) { for (TCpu* cpu : Pools[pool].WakeOrderCpus) { @@ -1247,11 +1247,11 @@ namespace NActors { // Cpu has not been woken up } - + inline void TUnitedWorkers::BeginExecution(TPoolId pool, ui32& activation, ui64 revolvingCounter) { Pools[pool].BeginExecution(activation, revolvingCounter); - } - + } + inline bool TUnitedWorkers::NextExecution(TPoolId pool, ui32& activation, ui64 revolvingCounter) { return Pools[pool].NextExecution(activation, revolvingCounter); } diff --git a/library/cpp/actors/core/executor_pool_united_ut.cpp b/library/cpp/actors/core/executor_pool_united_ut.cpp index d4df17f1b8..ddc4c348ee 100644 --- a/library/cpp/actors/core/executor_pool_united_ut.cpp +++ b/library/cpp/actors/core/executor_pool_united_ut.cpp @@ -58,8 +58,8 @@ public: if (GetCounter() == 0) { break; } - - Sleep(TDuration::MilliSeconds(1)); + + Sleep(TDuration::MilliSeconds(1)); } } @@ -78,8 +78,8 @@ private: void Handle(TEvMsg::TPtr &ev) { Y_UNUSED(ev); Action(); - TAtomicBase count = AtomicDecrement(Counter); - Y_VERIFY(count != Max<TAtomicBase>()); + TAtomicBase count = AtomicDecrement(Counter); + Y_VERIFY(count != Max<TAtomicBase>()); if (count) { Send(Receiver, new TEvMsg()); } @@ -149,9 +149,9 @@ Y_UNIT_TEST_SUITE(UnitedExecutorPool) { while (actor->GetCounter()) { auto now = TInstant::Now(); - UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Counter is " << actor->GetCounter()); - - Sleep(TDuration::MilliSeconds(1)); + UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "Counter is " << actor->GetCounter()); + + Sleep(TDuration::MilliSeconds(1)); } TVector<TExecutorThreadStats> stats; @@ -212,11 +212,11 @@ Y_UNIT_TEST_SUITE(UnitedExecutorPool) { left += actor->GetCounter(); } if (left == 0) { - break; - } + break; + } auto now = TInstant::Now(); UNIT_ASSERT_C(now - begin < TDuration::Seconds(5), "left " << left); - Sleep(TDuration::MilliSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); } for (size_t pool = 0; pool < pools; pool++) { @@ -311,11 +311,11 @@ Y_UNIT_TEST_SUITE(UnitedExecutorPool) { left += actor->GetCounter(); } if (left == 0) { - break; - } + break; + } auto now = TInstant::Now(); UNIT_ASSERT_C(now - begin < TDuration::Seconds(15), "left " << left); - Sleep(TDuration::MilliSeconds(1)); + Sleep(TDuration::MilliSeconds(1)); } for (size_t pool = 0; pool < pools; pool++) { diff --git a/library/cpp/actors/core/executor_thread.cpp b/library/cpp/actors/core/executor_thread.cpp index 446b651efd..7e961584e6 100644 --- a/library/cpp/actors/core/executor_thread.cpp +++ b/library/cpp/actors/core/executor_thread.cpp @@ -76,11 +76,11 @@ namespace NActors { Ctx.Executor->Schedule(deadline, ev, cookie, Ctx.WorkerId); } - void TExecutorThread::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) { - ++CurrentActorScheduledEventsCounter; + void TExecutorThread::Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) { + ++CurrentActorScheduledEventsCounter; Ctx.Executor->Schedule(deadline, ev, cookie, Ctx.WorkerId); - } - + } + void TExecutorThread::Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie) { ++CurrentActorScheduledEventsCounter; Ctx.Executor->Schedule(delta, ev, cookie, Ctx.WorkerId); @@ -145,7 +145,7 @@ namespace NActors { NHPTimer::STime hpnow; recipient = ev->GetRecipientRewrite(); if (actor = mailbox->FindActor(recipient.LocalId())) { - TActorContext ctx(*mailbox, *this, hpprev, recipient); + TActorContext ctx(*mailbox, *this, hpprev, recipient); TlsActivationContext = &ctx; #ifdef USE_ACTOR_CALLSTACK diff --git a/library/cpp/actors/core/executor_thread.h b/library/cpp/actors/core/executor_thread.h index 9d3c573f0d..72b8f28c1d 100644 --- a/library/cpp/actors/core/executor_thread.h +++ b/library/cpp/actors/core/executor_thread.h @@ -47,7 +47,7 @@ namespace NActors { const std::vector<THolder<IActor>>& GetUnregistered() const { return DyingActors; } void Schedule(TInstant deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); - void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); + void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); void Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr); bool Send(TAutoPtr<IEventHandle> ev) { diff --git a/library/cpp/actors/core/log.cpp b/library/cpp/actors/core/log.cpp index 5f63b5af58..391844dcff 100644 --- a/library/cpp/actors/core/log.cpp +++ b/library/cpp/actors/core/log.cpp @@ -223,9 +223,9 @@ namespace NActors { } } - void TLoggerActor::Throttle(const NLog::TSettings& settings) { + void TLoggerActor::Throttle(const NLog::TSettings& settings) { if (AtomicGet(IsOverflow)) - Sleep(settings.ThrottleDelay); + Sleep(settings.ThrottleDelay); } void TLoggerActor::LogIgnoredCount(TInstant now) { diff --git a/library/cpp/actors/core/log.h b/library/cpp/actors/core/log.h index c11a7cf3c1..f063642ad2 100644 --- a/library/cpp/actors/core/log.h +++ b/library/cpp/actors/core/log.h @@ -232,7 +232,7 @@ namespace NActors { // Directly call logger instead of sending a message void Log(TInstant time, NLog::EPriority priority, NLog::EComponent component, const char* c, ...); - static void Throttle(const NLog::TSettings& settings); + static void Throttle(const NLog::TSettings& settings); private: TIntrusivePtr<NLog::TSettings> Settings; @@ -322,7 +322,7 @@ namespace NActors { inline void DeliverLogMessage(TCtx& ctx, NLog::EPriority mPriority, NLog::EComponent mComponent, TString &&str) { const NLog::TSettings *mSettings = ctx.LoggerSettings(); - TLoggerActor::Throttle(*mSettings); + TLoggerActor::Throttle(*mSettings); ctx.Send(new IEventHandle(mSettings->LoggerActorId, TActorId(), new NLog::TEvLog(mPriority, mComponent, std::move(str)))); } diff --git a/library/cpp/actors/core/log_settings.cpp b/library/cpp/actors/core/log_settings.cpp index f52f2fc5d2..d77688021d 100644 --- a/library/cpp/actors/core/log_settings.cpp +++ b/library/cpp/actors/core/log_settings.cpp @@ -12,7 +12,7 @@ namespace NActors { , LoggerComponent(loggerComponent) , TimeThresholdMs(timeThresholdMs) , AllowDrop(true) - , ThrottleDelay(TDuration::MilliSeconds(100)) + , ThrottleDelay(TDuration::MilliSeconds(100)) , MinVal(0) , MaxVal(0) , Mask(0) @@ -34,7 +34,7 @@ namespace NActors { , LoggerComponent(loggerComponent) , TimeThresholdMs(timeThresholdMs) , AllowDrop(true) - , ThrottleDelay(TDuration::MilliSeconds(100)) + , ThrottleDelay(TDuration::MilliSeconds(100)) , MinVal(0) , MaxVal(0) , Mask(0) @@ -205,10 +205,10 @@ namespace NActors { AllowDrop = val; } - void TSettings::SetThrottleDelay(TDuration value) { - ThrottleDelay = value; - } - + void TSettings::SetThrottleDelay(TDuration value) { + ThrottleDelay = value; + } + void TSettings::SetUseLocalTimestamps(bool value) { UseLocalTimestamps = value; } diff --git a/library/cpp/actors/core/log_settings.h b/library/cpp/actors/core/log_settings.h index 7fe4504edd..5f4898bda1 100644 --- a/library/cpp/actors/core/log_settings.h +++ b/library/cpp/actors/core/log_settings.h @@ -73,7 +73,7 @@ namespace NActors { EComponent LoggerComponent; ui64 TimeThresholdMs; bool AllowDrop; - TDuration ThrottleDelay; + TDuration ThrottleDelay; TArrayHolder<TAtomic> ComponentInfo; TVector<TString> ComponentNames; EComponent MinVal; @@ -162,7 +162,7 @@ namespace NActors { static bool IsValidPriority(EPriority priority); bool IsValidComponent(EComponent component); void SetAllowDrop(bool val); - void SetThrottleDelay(TDuration value); + void SetThrottleDelay(TDuration value); void SetUseLocalTimestamps(bool value); private: diff --git a/library/cpp/actors/core/mailbox.cpp b/library/cpp/actors/core/mailbox.cpp index d84b4f9e46..b63577c7d6 100644 --- a/library/cpp/actors/core/mailbox.cpp +++ b/library/cpp/actors/core/mailbox.cpp @@ -529,7 +529,7 @@ namespace NActors { Y_FAIL(); } - AtomicStore(Lines + lineIndex, header); + AtomicStore(Lines + lineIndex, header); ui32 ret = lineIndexMask | 1; diff --git a/library/cpp/actors/core/mailbox.h b/library/cpp/actors/core/mailbox.h index 0bd9c4d314..8f4f901bd9 100644 --- a/library/cpp/actors/core/mailbox.h +++ b/library/cpp/actors/core/mailbox.h @@ -277,7 +277,7 @@ namespace NActors { TAtomic LastAllocatedLine; TAtomic AllocatedMailboxCount; - typedef TUnorderedCache<ui32, 512, 4> TMailboxCache; + typedef TUnorderedCache<ui32, 512, 4> TMailboxCache; TMailboxCache MailboxCacheSimple; TAtomic CachedSimpleMailboxes; TMailboxCache MailboxCacheRevolving; diff --git a/library/cpp/actors/core/monotonic.cpp b/library/cpp/actors/core/monotonic.cpp index 3465149dbe..eefd8913cc 100644 --- a/library/cpp/actors/core/monotonic.cpp +++ b/library/cpp/actors/core/monotonic.cpp @@ -1,23 +1,23 @@ -#include "monotonic.h" - -#include <chrono> - -namespace NActors { - - namespace { - // Unfortunately time_since_epoch() is sometimes negative on wine - // Remember initial time point at program start and use offsets from that - std::chrono::steady_clock::time_point MonotonicOffset = std::chrono::steady_clock::now(); - } - - ui64 GetMonotonicMicroSeconds() { - auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - MonotonicOffset).count(); - // Steady clock is supposed to never jump backwards, but it's better to be safe in case of buggy implementations - if (Y_UNLIKELY(microseconds < 0)) { - microseconds = 0; - } - // Add one so we never return zero - return microseconds + 1; - } - -} // namespace NActors +#include "monotonic.h" + +#include <chrono> + +namespace NActors { + + namespace { + // Unfortunately time_since_epoch() is sometimes negative on wine + // Remember initial time point at program start and use offsets from that + std::chrono::steady_clock::time_point MonotonicOffset = std::chrono::steady_clock::now(); + } + + ui64 GetMonotonicMicroSeconds() { + auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - MonotonicOffset).count(); + // Steady clock is supposed to never jump backwards, but it's better to be safe in case of buggy implementations + if (Y_UNLIKELY(microseconds < 0)) { + microseconds = 0; + } + // Add one so we never return zero + return microseconds + 1; + } + +} // namespace NActors diff --git a/library/cpp/actors/core/monotonic.h b/library/cpp/actors/core/monotonic.h index 6fceb91dbe..cc0136b558 100644 --- a/library/cpp/actors/core/monotonic.h +++ b/library/cpp/actors/core/monotonic.h @@ -1,111 +1,111 @@ -#pragma once - -#include <util/datetime/base.h> - -namespace NActors { - - /** - * Returns current monotonic time in microseconds - */ - ui64 GetMonotonicMicroSeconds(); - - /** - * Similar to TInstant, but measuring monotonic time - */ - class TMonotonic : public TTimeBase<TMonotonic> { - using TBase = TTimeBase<TMonotonic>; - - private: - constexpr explicit TMonotonic(TValue value) noexcept - : TBase(value) - { } - - public: - constexpr TMonotonic() noexcept { - } - - static constexpr TMonotonic FromValue(TValue value) noexcept { - return TMonotonic(value); - } - - static inline TMonotonic Now() { - return TMonotonic::MicroSeconds(GetMonotonicMicroSeconds()); - } - - using TBase::Days; - using TBase::Hours; - using TBase::MicroSeconds; - using TBase::MilliSeconds; - using TBase::Minutes; - using TBase::Seconds; - - static constexpr TMonotonic Max() noexcept { - return TMonotonic(::Max<ui64>()); - } - - static constexpr TMonotonic Zero() noexcept { - return TMonotonic(); - } - - static constexpr TMonotonic MicroSeconds(ui64 us) noexcept { - return TMonotonic(TInstant::MicroSeconds(us).GetValue()); - } - - static constexpr TMonotonic MilliSeconds(ui64 ms) noexcept { - return TMonotonic(TInstant::MilliSeconds(ms).GetValue()); - } - - static constexpr TMonotonic Seconds(ui64 s) noexcept { - return TMonotonic(TInstant::Seconds(s).GetValue()); - } - - static constexpr TMonotonic Minutes(ui64 m) noexcept { - return TMonotonic(TInstant::Minutes(m).GetValue()); - } - - static constexpr TMonotonic Hours(ui64 h) noexcept { - return TMonotonic(TInstant::Hours(h).GetValue()); - } - - static constexpr TMonotonic Days(ui64 d) noexcept { - return TMonotonic(TInstant::Days(d).GetValue()); - } - - template<class T> - inline TMonotonic& operator+=(const T& t) noexcept { - return (*this = (*this + t)); - } - - template<class T> - inline TMonotonic& operator-=(const T& t) noexcept { - return (*this = (*this - t)); - } - }; -} // namespace NActors - -Y_DECLARE_PODTYPE(NActors::TMonotonic); - -template<> -struct THash<NActors::TMonotonic> { - size_t operator()(const NActors::TMonotonic& key) const { - return THash<NActors::TMonotonic::TValue>()(key.GetValue()); - } -}; - -namespace NActors { - - constexpr TDuration operator-(const TMonotonic& l, const TMonotonic& r) { - return TInstant::FromValue(l.GetValue()) - TInstant::FromValue(r.GetValue()); - } - - constexpr TMonotonic operator+(const TMonotonic& l, const TDuration& r) { - TInstant result = TInstant::FromValue(l.GetValue()) + r; - return TMonotonic::FromValue(result.GetValue()); - } - - constexpr TMonotonic operator-(const TMonotonic& l, const TDuration& r) { - TInstant result = TInstant::FromValue(l.GetValue()) - r; - return TMonotonic::FromValue(result.GetValue()); - } - -} // namespace NActors +#pragma once + +#include <util/datetime/base.h> + +namespace NActors { + + /** + * Returns current monotonic time in microseconds + */ + ui64 GetMonotonicMicroSeconds(); + + /** + * Similar to TInstant, but measuring monotonic time + */ + class TMonotonic : public TTimeBase<TMonotonic> { + using TBase = TTimeBase<TMonotonic>; + + private: + constexpr explicit TMonotonic(TValue value) noexcept + : TBase(value) + { } + + public: + constexpr TMonotonic() noexcept { + } + + static constexpr TMonotonic FromValue(TValue value) noexcept { + return TMonotonic(value); + } + + static inline TMonotonic Now() { + return TMonotonic::MicroSeconds(GetMonotonicMicroSeconds()); + } + + using TBase::Days; + using TBase::Hours; + using TBase::MicroSeconds; + using TBase::MilliSeconds; + using TBase::Minutes; + using TBase::Seconds; + + static constexpr TMonotonic Max() noexcept { + return TMonotonic(::Max<ui64>()); + } + + static constexpr TMonotonic Zero() noexcept { + return TMonotonic(); + } + + static constexpr TMonotonic MicroSeconds(ui64 us) noexcept { + return TMonotonic(TInstant::MicroSeconds(us).GetValue()); + } + + static constexpr TMonotonic MilliSeconds(ui64 ms) noexcept { + return TMonotonic(TInstant::MilliSeconds(ms).GetValue()); + } + + static constexpr TMonotonic Seconds(ui64 s) noexcept { + return TMonotonic(TInstant::Seconds(s).GetValue()); + } + + static constexpr TMonotonic Minutes(ui64 m) noexcept { + return TMonotonic(TInstant::Minutes(m).GetValue()); + } + + static constexpr TMonotonic Hours(ui64 h) noexcept { + return TMonotonic(TInstant::Hours(h).GetValue()); + } + + static constexpr TMonotonic Days(ui64 d) noexcept { + return TMonotonic(TInstant::Days(d).GetValue()); + } + + template<class T> + inline TMonotonic& operator+=(const T& t) noexcept { + return (*this = (*this + t)); + } + + template<class T> + inline TMonotonic& operator-=(const T& t) noexcept { + return (*this = (*this - t)); + } + }; +} // namespace NActors + +Y_DECLARE_PODTYPE(NActors::TMonotonic); + +template<> +struct THash<NActors::TMonotonic> { + size_t operator()(const NActors::TMonotonic& key) const { + return THash<NActors::TMonotonic::TValue>()(key.GetValue()); + } +}; + +namespace NActors { + + constexpr TDuration operator-(const TMonotonic& l, const TMonotonic& r) { + return TInstant::FromValue(l.GetValue()) - TInstant::FromValue(r.GetValue()); + } + + constexpr TMonotonic operator+(const TMonotonic& l, const TDuration& r) { + TInstant result = TInstant::FromValue(l.GetValue()) + r; + return TMonotonic::FromValue(result.GetValue()); + } + + constexpr TMonotonic operator-(const TMonotonic& l, const TDuration& r) { + TInstant result = TInstant::FromValue(l.GetValue()) - r; + return TMonotonic::FromValue(result.GetValue()); + } + +} // namespace NActors diff --git a/library/cpp/actors/core/scheduler_actor.cpp b/library/cpp/actors/core/scheduler_actor.cpp index febc5e40dd..c189653e99 100644 --- a/library/cpp/actors/core/scheduler_actor.cpp +++ b/library/cpp/actors/core/scheduler_actor.cpp @@ -43,7 +43,7 @@ namespace NActors { TPollerToken::TPtr PollerToken; ui64 RealTime; - ui64 MonotonicTime; + ui64 MonotonicTime; ui64 ActiveTick; typedef TMap<ui64, TAutoPtr<NSchedulerQueue::TQueueType>> TMomentMap; // intrasecond queues @@ -56,7 +56,7 @@ namespace NActors { static const ui64 IntrasecondThreshold = 1048576; // ~second TAutoPtr<TMomentMap> ActiveSec; volatile ui64* CurrentTimestamp = nullptr; - volatile ui64* CurrentMonotonic = nullptr; + volatile ui64* CurrentMonotonic = nullptr; TDeque<TAutoPtr<IEventHandle>> EventsToBeSent; public: @@ -84,9 +84,9 @@ namespace NActors { Y_ASSERT(evInitialize.CurrentTimestamp != nullptr); CurrentTimestamp = evInitialize.CurrentTimestamp; - Y_ASSERT(evInitialize.CurrentMonotonic != nullptr); - CurrentMonotonic = evInitialize.CurrentMonotonic; - + Y_ASSERT(evInitialize.CurrentMonotonic != nullptr); + CurrentMonotonic = evInitialize.CurrentMonotonic; + struct itimerspec new_time; memset(&new_time, 0, sizeof(new_time)); new_time.it_value.tv_nsec = Cfg.ResolutionMicroseconds * 1000; @@ -96,10 +96,10 @@ namespace NActors { const bool success = ctx.Send(PollerActor, new TEvPollerRegister(TimerDescriptor, SelfId(), {})); Y_VERIFY(success); - RealTime = RelaxedLoad(CurrentTimestamp); - MonotonicTime = RelaxedLoad(CurrentMonotonic); + RealTime = RelaxedLoad(CurrentTimestamp); + MonotonicTime = RelaxedLoad(CurrentMonotonic); - ActiveTick = AlignUp<ui64>(MonotonicTime, IntrasecondThreshold); + ActiveTick = AlignUp<ui64>(MonotonicTime, IntrasecondThreshold); } void Handle(TEvPollerRegisterResult::TPtr ev, const TActorContext& ctx) { @@ -108,10 +108,10 @@ namespace NActors { } void UpdateTime() { - RealTime = TInstant::Now().MicroSeconds(); - MonotonicTime = Max(MonotonicTime, GetMonotonicMicroSeconds()); - AtomicStore(CurrentTimestamp, RealTime); - AtomicStore(CurrentMonotonic, MonotonicTime); + RealTime = TInstant::Now().MicroSeconds(); + MonotonicTime = Max(MonotonicTime, GetMonotonicMicroSeconds()); + AtomicStore(CurrentTimestamp, RealTime); + AtomicStore(CurrentMonotonic, MonotonicTime); } void TryUpdateTime(NHPTimer::STime* lastTimeUpdate) { diff --git a/library/cpp/actors/core/scheduler_actor.h b/library/cpp/actors/core/scheduler_actor.h index c2c561b43d..4209db0ab6 100644 --- a/library/cpp/actors/core/scheduler_actor.h +++ b/library/cpp/actors/core/scheduler_actor.h @@ -9,12 +9,12 @@ namespace NActors { struct TEvSchedulerInitialize : TEventLocal<TEvSchedulerInitialize, TEvents::TSystem::Bootstrap> { TVector<NSchedulerQueue::TReader*> ScheduleReaders; volatile ui64* CurrentTimestamp; - volatile ui64* CurrentMonotonic; + volatile ui64* CurrentMonotonic; - TEvSchedulerInitialize(const TVector<NSchedulerQueue::TReader*>& scheduleReaders, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) + TEvSchedulerInitialize(const TVector<NSchedulerQueue::TReader*>& scheduleReaders, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) : ScheduleReaders(scheduleReaders) , CurrentTimestamp(currentTimestamp) - , CurrentMonotonic(currentMonotonic) + , CurrentMonotonic(currentMonotonic) { } }; diff --git a/library/cpp/actors/core/scheduler_basic.cpp b/library/cpp/actors/core/scheduler_basic.cpp index fba200e16b..b0c80eb6d2 100644 --- a/library/cpp/actors/core/scheduler_basic.cpp +++ b/library/cpp/actors/core/scheduler_basic.cpp @@ -9,35 +9,35 @@ #endif namespace NActors { - - struct TBasicSchedulerThread::TMonCounters { - NMonitoring::TDynamicCounters::TCounterPtr TimeDelayMs; - NMonitoring::TDynamicCounters::TCounterPtr QueueSize; - NMonitoring::TDynamicCounters::TCounterPtr EventsSent; - NMonitoring::TDynamicCounters::TCounterPtr EventsDropped; - NMonitoring::TDynamicCounters::TCounterPtr EventsAdded; - NMonitoring::TDynamicCounters::TCounterPtr Iterations; - NMonitoring::TDynamicCounters::TCounterPtr Sleeps; - NMonitoring::TDynamicCounters::TCounterPtr ElapsedMicrosec; - - TMonCounters(const NMonitoring::TDynamicCounterPtr& counters) - : TimeDelayMs(counters->GetCounter("Scheduler/TimeDelayMs", false)) - , QueueSize(counters->GetCounter("Scheduler/QueueSize", false)) - , EventsSent(counters->GetCounter("Scheduler/EventsSent", true)) - , EventsDropped(counters->GetCounter("Scheduler/EventsDropped", true)) - , EventsAdded(counters->GetCounter("Scheduler/EventsAdded", true)) - , Iterations(counters->GetCounter("Scheduler/Iterations", true)) - , Sleeps(counters->GetCounter("Scheduler/Sleeps", true)) - , ElapsedMicrosec(counters->GetCounter("Scheduler/ElapsedMicrosec", true)) - { } - }; - + + struct TBasicSchedulerThread::TMonCounters { + NMonitoring::TDynamicCounters::TCounterPtr TimeDelayMs; + NMonitoring::TDynamicCounters::TCounterPtr QueueSize; + NMonitoring::TDynamicCounters::TCounterPtr EventsSent; + NMonitoring::TDynamicCounters::TCounterPtr EventsDropped; + NMonitoring::TDynamicCounters::TCounterPtr EventsAdded; + NMonitoring::TDynamicCounters::TCounterPtr Iterations; + NMonitoring::TDynamicCounters::TCounterPtr Sleeps; + NMonitoring::TDynamicCounters::TCounterPtr ElapsedMicrosec; + + TMonCounters(const NMonitoring::TDynamicCounterPtr& counters) + : TimeDelayMs(counters->GetCounter("Scheduler/TimeDelayMs", false)) + , QueueSize(counters->GetCounter("Scheduler/QueueSize", false)) + , EventsSent(counters->GetCounter("Scheduler/EventsSent", true)) + , EventsDropped(counters->GetCounter("Scheduler/EventsDropped", true)) + , EventsAdded(counters->GetCounter("Scheduler/EventsAdded", true)) + , Iterations(counters->GetCounter("Scheduler/Iterations", true)) + , Sleeps(counters->GetCounter("Scheduler/Sleeps", true)) + , ElapsedMicrosec(counters->GetCounter("Scheduler/ElapsedMicrosec", true)) + { } + }; + TBasicSchedulerThread::TBasicSchedulerThread(const TSchedulerConfig& config) : Config(config) - , MonCounters(Config.MonCounters ? new TMonCounters(Config.MonCounters) : nullptr) + , MonCounters(Config.MonCounters ? new TMonCounters(Config.MonCounters) : nullptr) , ActorSystem(nullptr) , CurrentTimestamp(nullptr) - , CurrentMonotonic(nullptr) + , CurrentMonotonic(nullptr) , TotalReaders(0) , StopFlag(false) , ScheduleMap(3600) @@ -55,45 +55,45 @@ namespace NActors { #endif ::SetCurrentThreadName("Scheduler"); - ui64 currentMonotonic = RelaxedLoad(CurrentMonotonic); - ui64 throttledMonotonic = currentMonotonic; + ui64 currentMonotonic = RelaxedLoad(CurrentMonotonic); + ui64 throttledMonotonic = currentMonotonic; - ui64 activeTick = AlignUp<ui64>(throttledMonotonic, IntrasecondThreshold); + ui64 activeTick = AlignUp<ui64>(throttledMonotonic, IntrasecondThreshold); TAutoPtr<TMomentMap> activeSec; NHPTimer::STime hpprev = GetCycleCountFast(); - ui64 nextTimestamp = TInstant::Now().MicroSeconds(); - ui64 nextMonotonic = Max(currentMonotonic, GetMonotonicMicroSeconds()); - + ui64 nextTimestamp = TInstant::Now().MicroSeconds(); + ui64 nextMonotonic = Max(currentMonotonic, GetMonotonicMicroSeconds()); + while (!AtomicLoad(&StopFlag)) { { - const ui64 delta = nextMonotonic - throttledMonotonic; - const ui64 elapsedDelta = nextMonotonic - currentMonotonic; - const ui64 threshold = Max(Min(Config.ProgressThreshold, 2 * elapsedDelta), ui64(1)); - - throttledMonotonic = (delta > threshold) ? throttledMonotonic + threshold : nextMonotonic; - - if (MonCounters) { - *MonCounters->TimeDelayMs = (nextMonotonic - throttledMonotonic) / 1000; - } - } - AtomicStore(CurrentTimestamp, nextTimestamp); - AtomicStore(CurrentMonotonic, nextMonotonic); - currentMonotonic = nextMonotonic; - - if (MonCounters) { - ++*MonCounters->Iterations; + const ui64 delta = nextMonotonic - throttledMonotonic; + const ui64 elapsedDelta = nextMonotonic - currentMonotonic; + const ui64 threshold = Max(Min(Config.ProgressThreshold, 2 * elapsedDelta), ui64(1)); + + throttledMonotonic = (delta > threshold) ? throttledMonotonic + threshold : nextMonotonic; + + if (MonCounters) { + *MonCounters->TimeDelayMs = (nextMonotonic - throttledMonotonic) / 1000; + } } - + AtomicStore(CurrentTimestamp, nextTimestamp); + AtomicStore(CurrentMonotonic, nextMonotonic); + currentMonotonic = nextMonotonic; + + if (MonCounters) { + ++*MonCounters->Iterations; + } + bool somethingDone = false; // first step - send everything triggered on schedule - ui64 eventsSent = 0; - ui64 eventsDropped = 0; + ui64 eventsSent = 0; + ui64 eventsDropped = 0; for (;;) { while (!!activeSec && !activeSec->empty()) { TMomentMap::iterator it = activeSec->begin(); - if (it->first <= throttledMonotonic) { + if (it->first <= throttledMonotonic) { if (NSchedulerQueue::TQueueType* q = it->second.Get()) { while (NSchedulerQueue::TEntry* x = q->Reader.Pop()) { somethingDone = true; @@ -102,16 +102,16 @@ namespace NActors { ISchedulerCookie* cookie = x->Cookie; // TODO: lazy send with backoff queue to not hang over contended mailboxes if (cookie) { - if (cookie->Detach()) { + if (cookie->Detach()) { ActorSystem->Send(ev); - ++eventsSent; - } else { + ++eventsSent; + } else { delete ev; - ++eventsDropped; - } + ++eventsDropped; + } } else { ActorSystem->Send(ev); - ++eventsSent; + ++eventsSent; } } } @@ -120,7 +120,7 @@ namespace NActors { break; } - if (activeTick <= throttledMonotonic) { + if (activeTick <= throttledMonotonic) { Y_VERIFY_DEBUG(!activeSec || activeSec->empty()); activeSec.Destroy(); activeTick += IntrasecondThreshold; @@ -138,7 +138,7 @@ namespace NActors { // second step - collect everything from queues - ui64 eventsAdded = 0; + ui64 eventsAdded = 0; for (ui32 i = 0; i != TotalReaders; ++i) { while (NSchedulerQueue::TEntry* x = Readers[i]->Pop()) { somethingDone = true; @@ -165,57 +165,57 @@ namespace NActors { queue.Reset(new NSchedulerQueue::TQueueType()); queue->Writer.Push(instant, ev, cookie); } - - ++eventsAdded; + + ++eventsAdded; } } NHPTimer::STime hpnow = GetCycleCountFast(); - - if (MonCounters) { - *MonCounters->QueueSize -= eventsSent + eventsDropped; - *MonCounters->QueueSize += eventsAdded; - *MonCounters->EventsSent += eventsSent; - *MonCounters->EventsDropped += eventsDropped; - *MonCounters->EventsAdded += eventsAdded; - *MonCounters->ElapsedMicrosec += NHPTimer::GetSeconds(hpnow - hpprev) * 1000000; - } - - hpprev = hpnow; - nextTimestamp = TInstant::Now().MicroSeconds(); - nextMonotonic = Max(currentMonotonic, GetMonotonicMicroSeconds()); - + + if (MonCounters) { + *MonCounters->QueueSize -= eventsSent + eventsDropped; + *MonCounters->QueueSize += eventsAdded; + *MonCounters->EventsSent += eventsSent; + *MonCounters->EventsDropped += eventsDropped; + *MonCounters->EventsAdded += eventsAdded; + *MonCounters->ElapsedMicrosec += NHPTimer::GetSeconds(hpnow - hpprev) * 1000000; + } + + hpprev = hpnow; + nextTimestamp = TInstant::Now().MicroSeconds(); + nextMonotonic = Max(currentMonotonic, GetMonotonicMicroSeconds()); + // ok complete, if nothing left - sleep if (!somethingDone) { - const ui64 nextInstant = AlignDown<ui64>(throttledMonotonic + Config.ResolutionMicroseconds, Config.ResolutionMicroseconds); - if (nextMonotonic >= nextInstant) // already in next time-slice + const ui64 nextInstant = AlignDown<ui64>(throttledMonotonic + Config.ResolutionMicroseconds, Config.ResolutionMicroseconds); + if (nextMonotonic >= nextInstant) // already in next time-slice continue; - const ui64 delta = nextInstant - nextMonotonic; + const ui64 delta = nextInstant - nextMonotonic; if (delta < Config.SpinThreshold) // not so much time left, just spin continue; - if (MonCounters) { - ++*MonCounters->Sleeps; - } - + if (MonCounters) { + ++*MonCounters->Sleeps; + } + NanoSleep(delta * 1000); // ok, looks like we should sleep a bit. - - // Don't count sleep in elapsed microseconds + + // Don't count sleep in elapsed microseconds hpprev = GetCycleCountFast(); - nextTimestamp = TInstant::Now().MicroSeconds(); - nextMonotonic = Max(currentMonotonic, GetMonotonicMicroSeconds()); + nextTimestamp = TInstant::Now().MicroSeconds(); + nextMonotonic = Max(currentMonotonic, GetMonotonicMicroSeconds()); } } // ok, die! } - void TBasicSchedulerThread::Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) { + void TBasicSchedulerThread::Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) { ActorSystem = actorSystem; CurrentTimestamp = currentTimestamp; - CurrentMonotonic = currentMonotonic; - *CurrentTimestamp = TInstant::Now().MicroSeconds(); - *CurrentMonotonic = GetMonotonicMicroSeconds(); + CurrentMonotonic = currentMonotonic; + *CurrentTimestamp = TInstant::Now().MicroSeconds(); + *CurrentMonotonic = GetMonotonicMicroSeconds(); } void TBasicSchedulerThread::PrepareSchedules(NSchedulerQueue::TReader** readers, ui32 scheduleReadersCount) { @@ -225,16 +225,16 @@ namespace NActors { Copy(readers, readers + scheduleReadersCount, Readers.Get()); } - void TBasicSchedulerThread::PrepareStart() { - // Called after actor system is initialized, but before executor threads - // are started, giving us a chance to update current timestamp with a - // more recent value, taking initialization time into account. This is - // safe to do, since scheduler thread is not started yet, so no other - // threads are updating time concurrently. - AtomicStore(CurrentTimestamp, TInstant::Now().MicroSeconds()); - AtomicStore(CurrentMonotonic, Max(RelaxedLoad(CurrentMonotonic), GetMonotonicMicroSeconds())); - } - + void TBasicSchedulerThread::PrepareStart() { + // Called after actor system is initialized, but before executor threads + // are started, giving us a chance to update current timestamp with a + // more recent value, taking initialization time into account. This is + // safe to do, since scheduler thread is not started yet, so no other + // threads are updating time concurrently. + AtomicStore(CurrentTimestamp, TInstant::Now().MicroSeconds()); + AtomicStore(CurrentMonotonic, Max(RelaxedLoad(CurrentMonotonic), GetMonotonicMicroSeconds())); + } + void TBasicSchedulerThread::Start() { MainCycle.Reset(new NThreading::TLegacyFuture<void, false>(std::bind(&TBasicSchedulerThread::CycleFunc, this))); } diff --git a/library/cpp/actors/core/scheduler_basic.h b/library/cpp/actors/core/scheduler_basic.h index 2ccde39235..89ef8323f5 100644 --- a/library/cpp/actors/core/scheduler_basic.h +++ b/library/cpp/actors/core/scheduler_basic.h @@ -1,7 +1,7 @@ #pragma once #include "actorsystem.h" -#include "monotonic.h" +#include "monotonic.h" #include "scheduler_queue.h" #include <library/cpp/actors/util/queue_chunk.h> #include <library/cpp/threading/future/legacy_future.h> @@ -9,17 +9,17 @@ #include <util/generic/map.h> namespace NActors { - + class TBasicSchedulerThread: public ISchedulerThread { // TODO: replace with NUMA-local threads and per-thread schedules const TSchedulerConfig Config; - struct TMonCounters; - const THolder<TMonCounters> MonCounters; - + struct TMonCounters; + const THolder<TMonCounters> MonCounters; + TActorSystem* ActorSystem; volatile ui64* CurrentTimestamp; - volatile ui64* CurrentMonotonic; + volatile ui64* CurrentMonotonic; ui32 TotalReaders; TArrayHolder<NSchedulerQueue::TReader*> Readers; @@ -44,7 +44,7 @@ namespace NActors { void Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) override; void PrepareSchedules(NSchedulerQueue::TReader** readers, ui32 scheduleReadersCount) override; - void PrepareStart() override; + void PrepareStart() override; void Start() override; void PrepareStop() override; void Stop() override; @@ -55,10 +55,10 @@ namespace NActors { virtual ~TMockSchedulerThread() override { } - void Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) override { + void Prepare(TActorSystem* actorSystem, volatile ui64* currentTimestamp, volatile ui64* currentMonotonic) override { Y_UNUSED(actorSystem); - *currentTimestamp = TInstant::Now().MicroSeconds(); - *currentMonotonic = GetMonotonicMicroSeconds(); + *currentTimestamp = TInstant::Now().MicroSeconds(); + *currentMonotonic = GetMonotonicMicroSeconds(); } void PrepareSchedules(NSchedulerQueue::TReader** readers, ui32 scheduleReadersCount) override { diff --git a/library/cpp/actors/core/scheduler_queue.h b/library/cpp/actors/core/scheduler_queue.h index 3b8fac28f0..51b597be1f 100644 --- a/library/cpp/actors/core/scheduler_queue.h +++ b/library/cpp/actors/core/scheduler_queue.h @@ -76,10 +76,10 @@ namespace NActors { } void Push(ui64 instantMicrosends, IEventHandle* ev, ISchedulerCookie* cookie) { - if (Y_UNLIKELY(instantMicrosends == 0)) { - // Protect against Pop() getting stuck forever - instantMicrosends = 1; - } + if (Y_UNLIKELY(instantMicrosends == 0)) { + // Protect against Pop() getting stuck forever + instantMicrosends = 1; + } if (WritePosition != TChunk::EntriesCount) { volatile TEntry& entry = WriteTo->Entries[WritePosition]; entry.Cookie = cookie; diff --git a/library/cpp/actors/core/ya.make b/library/cpp/actors/core/ya.make index 880a9d00db..25fc0ce902 100644 --- a/library/cpp/actors/core/ya.make +++ b/library/cpp/actors/core/ya.make @@ -81,7 +81,7 @@ SRCS( memory_tracker.h mon.h mon_stats.h - monotonic.cpp + monotonic.cpp monotonic.h worker_context.cpp worker_context.h diff --git a/library/cpp/actors/dnsresolver/dnsresolver.cpp b/library/cpp/actors/dnsresolver/dnsresolver.cpp index 6329bb0083..791360bd82 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver.cpp +++ b/library/cpp/actors/dnsresolver/dnsresolver.cpp @@ -1,475 +1,475 @@ -#include "dnsresolver.h" - -#include <library/cpp/actors/core/hfunc.h> -#include <library/cpp/threading/queue/mpsc_htswap.h> -#include <util/network/pair.h> -#include <util/network/socket.h> -#include <util/string/builder.h> -#include <util/system/thread.h> - -#include <ares.h> - -#include <atomic> - -namespace NActors { -namespace NDnsResolver { - - class TAresLibraryInitBase { - protected: - TAresLibraryInitBase() noexcept { - int status = ares_library_init(ARES_LIB_INIT_ALL); - Y_VERIFY(status == ARES_SUCCESS, "Unexpected failure to initialize c-ares library"); - } - - ~TAresLibraryInitBase() noexcept { - ares_library_cleanup(); - } - }; - - class TCallbackQueueBase { - protected: - TCallbackQueueBase() noexcept { - int err = SocketPair(Sockets, false, true); - Y_VERIFY(err == 0, "Unexpected failure to create a socket pair"); - SetNonBlock(Sockets[0]); - SetNonBlock(Sockets[1]); - } - - ~TCallbackQueueBase() noexcept { - closesocket(Sockets[0]); - closesocket(Sockets[1]); - } - - protected: - using TCallback = std::function<void()>; - using TCallbackQueue = NThreading::THTSwapQueue<TCallback>; - - void PushCallback(TCallback callback) { - Y_VERIFY(callback, "Cannot push an empty callback"); - CallbackQueue.Push(std::move(callback)); // this is a lockfree queue - - // Wake up worker thread on the first activation - if (Activations.fetch_add(1, std::memory_order_acq_rel) == 0) { - char ch = 'x'; - ssize_t ret; -#ifdef _win_ - ret = send(SignalSock(), &ch, 1, 0); - if (ret == -1) { - Y_VERIFY(WSAGetLastError() == WSAEWOULDBLOCK, "Unexpected send error"); - return; - } -#else - do { - ret = send(SignalSock(), &ch, 1, 0); - } while (ret == -1 && errno == EINTR); - if (ret == -1) { - Y_VERIFY(errno == EAGAIN || errno == EWOULDBLOCK, "Unexpected send error"); - return; - } -#endif - Y_VERIFY(ret == 1, "Unexpected send result"); - } - } - - void RunCallbacks() noexcept { - char ch[32]; - ssize_t ret; - bool signalled = false; - for (;;) { - ret = recv(WaitSock(), ch, sizeof(ch), 0); - if (ret > 0) { - signalled = true; - } - if (ret == sizeof(ch)) { - continue; - } - if (ret != -1) { - break; - } -#ifdef _win_ - if (WSAGetLastError() == WSAEWOULDBLOCK) { - break; - } - Y_FAIL("Unexpected recv error"); -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - Y_VERIFY(errno == EINTR, "Unexpected recv error"); -#endif - } - - if (signalled) { - // There's exactly one write to SignalSock while Activations != 0 - // It's impossible to get signalled while Activations == 0 - // We must set Activations = 0 to receive new signals - size_t count = Activations.exchange(0, std::memory_order_acq_rel); - Y_VERIFY(count != 0); - - // N.B. due to the way HTSwap works we may not be able to pop - // all callbacks on this activation, however we expect a new - // delayed activation to happen at a later time. - while (auto callback = CallbackQueue.Pop()) { - callback(); - } - } - } - - SOCKET SignalSock() { - return Sockets[0]; - } - - SOCKET WaitSock() { - return Sockets[1]; - } - - private: - SOCKET Sockets[2]; - TCallbackQueue CallbackQueue; - std::atomic<size_t> Activations{ 0 }; - }; - - class TSimpleDnsResolver - : public TActor<TSimpleDnsResolver> - , private TAresLibraryInitBase - , private TCallbackQueueBase - { - public: - TSimpleDnsResolver(TSimpleDnsResolverOptions options) noexcept - : TActor(&TThis::StateWork) - , Options(std::move(options)) - , WorkerThread(&TThis::WorkerThreadStart, this) - { - InitAres(); - - WorkerThread.Start(); - } - - ~TSimpleDnsResolver() noexcept override { - if (!Stopped) { - PushCallback([this] { - // Mark as stopped first - Stopped = true; - - // Cancel all current ares requests (will not send replies) - ares_cancel(AresChannel); - }); - - WorkerThread.Join(); - } - - StopAres(); - } - - static constexpr EActivityType ActorActivityType() { - return DNS_RESOLVER; - } - - private: - void InitAres() noexcept { - struct ares_options options; - memset(&options, 0, sizeof(options)); - int optmask = 0; - - options.flags = ARES_FLAG_STAYOPEN; - optmask |= ARES_OPT_FLAGS; - - options.sock_state_cb = &TThis::SockStateCallback; - options.sock_state_cb_data = this; - optmask |= ARES_OPT_SOCK_STATE_CB; - - options.timeout = Options.Timeout.MilliSeconds(); - if (options.timeout > 0) { - optmask |= ARES_OPT_TIMEOUTMS; - } - - options.tries = Options.Attempts; - if (options.tries > 0) { - optmask |= ARES_OPT_TRIES; - } - - int err = ares_init_options(&AresChannel, &options, optmask); - Y_VERIFY(err == 0, "Unexpected failure to initialize c-ares channel"); - - if (Options.Servers) { - TStringBuilder csv; - for (const TString& server : Options.Servers) { - if (csv) { - csv << ','; - } - csv << server; - } - err = ares_set_servers_ports_csv(AresChannel, csv.c_str()); - Y_VERIFY(err == 0, "Unexpected failure to set a list of dns servers: %s", ares_strerror(err)); - } - } - - void StopAres() noexcept { - // Destroy the ares channel - ares_destroy(AresChannel); - AresChannel = nullptr; - } - - private: - STRICT_STFUNC(StateWork, { - hFunc(TEvents::TEvPoison, Handle); - hFunc(TEvDns::TEvGetHostByName, Handle); - hFunc(TEvDns::TEvGetAddr, Handle); - }) - - void Handle(TEvents::TEvPoison::TPtr&) { - Y_VERIFY(!Stopped); - - PushCallback([this] { - // Cancel all current ares requests (will send notifications) - ares_cancel(AresChannel); - - // Mark as stopped last - Stopped = true; - }); - - WorkerThread.Join(); - PassAway(); - } - - private: - enum class ERequestType { - GetHostByName, - GetAddr, - }; - - struct TRequestContext : public TThrRefBase { - using TPtr = TIntrusivePtr<TRequestContext>; - - TThis* Self; - TActorSystem* ActorSystem; - TActorId SelfId; - TActorId Sender; - ui64 Cookie; - ERequestType Type; - - TRequestContext(TThis* self, TActorSystem* as, TActorId selfId, TActorId sender, ui64 cookie, ERequestType type) - : Self(self) - , ActorSystem(as) - , SelfId(selfId) - , Sender(sender) - , Cookie(cookie) - , Type(type) - { } - }; - - private: - void Handle(TEvDns::TEvGetHostByName::TPtr& ev) { - auto* msg = ev->Get(); - auto reqCtx = MakeIntrusive<TRequestContext>( - this, TActivationContext::ActorSystem(), SelfId(), ev->Sender, ev->Cookie, ERequestType::GetHostByName); - PushCallback([this, reqCtx = std::move(reqCtx), name = std::move(msg->Name), family = msg->Family] () mutable { - StartGetHostByName(std::move(reqCtx), std::move(name), family); - }); - } - - void Handle(TEvDns::TEvGetAddr::TPtr& ev) { - auto* msg = ev->Get(); - auto reqCtx = MakeIntrusive<TRequestContext>( - this, TActivationContext::ActorSystem(), SelfId(), ev->Sender, ev->Cookie, ERequestType::GetAddr); - PushCallback([this, reqCtx = std::move(reqCtx), name = std::move(msg->Name), family = msg->Family] () mutable { - StartGetHostByName(std::move(reqCtx), std::move(name), family); - }); - } - - void StartGetHostByName(TRequestContext::TPtr reqCtx, TString name, int family) noexcept { - reqCtx->Ref(); - ares_gethostbyname(AresChannel, name.c_str(), family, - &TThis::GetHostByNameAresCallback, reqCtx.Get()); - } - - private: - static void GetHostByNameAresCallback(void* arg, int status, int timeouts, struct hostent* info) { - Y_UNUSED(timeouts); - TRequestContext::TPtr reqCtx(static_cast<TRequestContext*>(arg)); - reqCtx->UnRef(); - - if (reqCtx->Self->Stopped) { - // Don't send any replies after destruction - return; - } - - switch (reqCtx->Type) { - case ERequestType::GetHostByName: { - auto result = MakeHolder<TEvDns::TEvGetHostByNameResult>(); - if (status == 0) { - switch (info->h_addrtype) { - case AF_INET: { - for (int i = 0; info->h_addr_list[i] != nullptr; ++i) { - result->AddrsV4.emplace_back(*(struct in_addr*)(info->h_addr_list[i])); - } - break; - } - case AF_INET6: { - for (int i = 0; info->h_addr_list[i] != nullptr; ++i) { - result->AddrsV6.emplace_back(*(struct in6_addr*)(info->h_addr_list[i])); - } - break; - } - default: - Y_FAIL("unknown address family in ares callback"); - } - } else { - result->ErrorText = ares_strerror(status); - } - result->Status = status; - - reqCtx->ActorSystem->Send(new IEventHandle(reqCtx->Sender, reqCtx->SelfId, result.Release(), 0, reqCtx->Cookie)); - break; - } - - case ERequestType::GetAddr: { - auto result = MakeHolder<TEvDns::TEvGetAddrResult>(); - if (status == 0 && Y_UNLIKELY(info->h_addr_list[0] == nullptr)) { - status = ARES_ENODATA; - } - if (status == 0) { - switch (info->h_addrtype) { - case AF_INET: { - result->Addr = *(struct in_addr*)(info->h_addr_list[0]); - break; - } - case AF_INET6: { - result->Addr = *(struct in6_addr*)(info->h_addr_list[0]); - break; - } - default: - Y_FAIL("unknown address family in ares callback"); - } - } else { - result->ErrorText = ares_strerror(status); - } - result->Status = status; - - reqCtx->ActorSystem->Send(new IEventHandle(reqCtx->Sender, reqCtx->SelfId, result.Release(), 0, reqCtx->Cookie)); - break; - } - } - } - - private: - static void SockStateCallback(void* data, ares_socket_t socket_fd, int readable, int writable) { - static_cast<TThis*>(data)->DoSockStateCallback(socket_fd, readable, writable); - } - - void DoSockStateCallback(ares_socket_t socket_fd, int readable, int writable) noexcept { - int events = (readable ? (POLLRDNORM | POLLIN) : 0) | (writable ? (POLLWRNORM | POLLOUT) : 0); - if (events == 0) { - AresSockStates.erase(socket_fd); - } else { - AresSockStates[socket_fd].NeededEvents = events; - } - } - - private: - static void* WorkerThreadStart(void* arg) noexcept { - static_cast<TSimpleDnsResolver*>(arg)->WorkerThreadLoop(); - return nullptr; - } - - void WorkerThreadLoop() noexcept { - TThread::SetCurrentThreadName("DnsResolver"); - - TVector<struct pollfd> fds; - while (!Stopped) { - fds.clear(); - fds.reserve(1 + AresSockStates.size()); - { - auto& entry = fds.emplace_back(); - entry.fd = WaitSock(); - entry.events = POLLRDNORM | POLLIN; - } - for (auto& kv : AresSockStates) { - auto& entry = fds.emplace_back(); - entry.fd = kv.first; - entry.events = kv.second.NeededEvents; - } - - int timeout = -1; - struct timeval tv; - if (ares_timeout(AresChannel, nullptr, &tv)) { - timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; - } - - int ret = poll(fds.data(), fds.size(), timeout); - if (ret == -1) { - if (errno == EINTR) { - continue; - } - // we cannot handle failures, run callbacks and pretend everything is ok - RunCallbacks(); - if (Stopped) { - break; - } - ret = 0; - } - - bool ares_called = false; - if (ret > 0) { - for (size_t i = 0; i < fds.size(); ++i) { - auto& entry = fds[i]; - - // Handle WaitSock activation and run callbacks - if (i == 0) { - if (entry.revents & (POLLRDNORM | POLLIN)) { - RunCallbacks(); - if (Stopped) { - break; - } - } - continue; - } - - // All other sockets belong to ares - if (entry.revents == 0) { - continue; - } - // Previous invocation of aress_process_fd might have removed some sockets - if (Y_UNLIKELY(!AresSockStates.contains(entry.fd))) { - continue; - } - ares_process_fd( - AresChannel, - entry.revents & (POLLRDNORM | POLLIN) ? entry.fd : ARES_SOCKET_BAD, - entry.revents & (POLLWRNORM | POLLOUT) ? entry.fd : ARES_SOCKET_BAD); - ares_called = true; - } - - if (Stopped) { - break; - } - } - - if (!ares_called) { - // Let ares handle timeouts - ares_process_fd(AresChannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - } - } - } - - private: - struct TSockState { - short NeededEvents = 0; // poll events - }; - - private: - TSimpleDnsResolverOptions Options; - TThread WorkerThread; - - ares_channel AresChannel; - THashMap<SOCKET, TSockState> AresSockStates; - - bool Stopped = false; - }; - - IActor* CreateSimpleDnsResolver(TSimpleDnsResolverOptions options) { - return new TSimpleDnsResolver(std::move(options)); - } - -} // namespace NDnsResolver -} // namespace NActors +#include "dnsresolver.h" + +#include <library/cpp/actors/core/hfunc.h> +#include <library/cpp/threading/queue/mpsc_htswap.h> +#include <util/network/pair.h> +#include <util/network/socket.h> +#include <util/string/builder.h> +#include <util/system/thread.h> + +#include <ares.h> + +#include <atomic> + +namespace NActors { +namespace NDnsResolver { + + class TAresLibraryInitBase { + protected: + TAresLibraryInitBase() noexcept { + int status = ares_library_init(ARES_LIB_INIT_ALL); + Y_VERIFY(status == ARES_SUCCESS, "Unexpected failure to initialize c-ares library"); + } + + ~TAresLibraryInitBase() noexcept { + ares_library_cleanup(); + } + }; + + class TCallbackQueueBase { + protected: + TCallbackQueueBase() noexcept { + int err = SocketPair(Sockets, false, true); + Y_VERIFY(err == 0, "Unexpected failure to create a socket pair"); + SetNonBlock(Sockets[0]); + SetNonBlock(Sockets[1]); + } + + ~TCallbackQueueBase() noexcept { + closesocket(Sockets[0]); + closesocket(Sockets[1]); + } + + protected: + using TCallback = std::function<void()>; + using TCallbackQueue = NThreading::THTSwapQueue<TCallback>; + + void PushCallback(TCallback callback) { + Y_VERIFY(callback, "Cannot push an empty callback"); + CallbackQueue.Push(std::move(callback)); // this is a lockfree queue + + // Wake up worker thread on the first activation + if (Activations.fetch_add(1, std::memory_order_acq_rel) == 0) { + char ch = 'x'; + ssize_t ret; +#ifdef _win_ + ret = send(SignalSock(), &ch, 1, 0); + if (ret == -1) { + Y_VERIFY(WSAGetLastError() == WSAEWOULDBLOCK, "Unexpected send error"); + return; + } +#else + do { + ret = send(SignalSock(), &ch, 1, 0); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + Y_VERIFY(errno == EAGAIN || errno == EWOULDBLOCK, "Unexpected send error"); + return; + } +#endif + Y_VERIFY(ret == 1, "Unexpected send result"); + } + } + + void RunCallbacks() noexcept { + char ch[32]; + ssize_t ret; + bool signalled = false; + for (;;) { + ret = recv(WaitSock(), ch, sizeof(ch), 0); + if (ret > 0) { + signalled = true; + } + if (ret == sizeof(ch)) { + continue; + } + if (ret != -1) { + break; + } +#ifdef _win_ + if (WSAGetLastError() == WSAEWOULDBLOCK) { + break; + } + Y_FAIL("Unexpected recv error"); +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + Y_VERIFY(errno == EINTR, "Unexpected recv error"); +#endif + } + + if (signalled) { + // There's exactly one write to SignalSock while Activations != 0 + // It's impossible to get signalled while Activations == 0 + // We must set Activations = 0 to receive new signals + size_t count = Activations.exchange(0, std::memory_order_acq_rel); + Y_VERIFY(count != 0); + + // N.B. due to the way HTSwap works we may not be able to pop + // all callbacks on this activation, however we expect a new + // delayed activation to happen at a later time. + while (auto callback = CallbackQueue.Pop()) { + callback(); + } + } + } + + SOCKET SignalSock() { + return Sockets[0]; + } + + SOCKET WaitSock() { + return Sockets[1]; + } + + private: + SOCKET Sockets[2]; + TCallbackQueue CallbackQueue; + std::atomic<size_t> Activations{ 0 }; + }; + + class TSimpleDnsResolver + : public TActor<TSimpleDnsResolver> + , private TAresLibraryInitBase + , private TCallbackQueueBase + { + public: + TSimpleDnsResolver(TSimpleDnsResolverOptions options) noexcept + : TActor(&TThis::StateWork) + , Options(std::move(options)) + , WorkerThread(&TThis::WorkerThreadStart, this) + { + InitAres(); + + WorkerThread.Start(); + } + + ~TSimpleDnsResolver() noexcept override { + if (!Stopped) { + PushCallback([this] { + // Mark as stopped first + Stopped = true; + + // Cancel all current ares requests (will not send replies) + ares_cancel(AresChannel); + }); + + WorkerThread.Join(); + } + + StopAres(); + } + + static constexpr EActivityType ActorActivityType() { + return DNS_RESOLVER; + } + + private: + void InitAres() noexcept { + struct ares_options options; + memset(&options, 0, sizeof(options)); + int optmask = 0; + + options.flags = ARES_FLAG_STAYOPEN; + optmask |= ARES_OPT_FLAGS; + + options.sock_state_cb = &TThis::SockStateCallback; + options.sock_state_cb_data = this; + optmask |= ARES_OPT_SOCK_STATE_CB; + + options.timeout = Options.Timeout.MilliSeconds(); + if (options.timeout > 0) { + optmask |= ARES_OPT_TIMEOUTMS; + } + + options.tries = Options.Attempts; + if (options.tries > 0) { + optmask |= ARES_OPT_TRIES; + } + + int err = ares_init_options(&AresChannel, &options, optmask); + Y_VERIFY(err == 0, "Unexpected failure to initialize c-ares channel"); + + if (Options.Servers) { + TStringBuilder csv; + for (const TString& server : Options.Servers) { + if (csv) { + csv << ','; + } + csv << server; + } + err = ares_set_servers_ports_csv(AresChannel, csv.c_str()); + Y_VERIFY(err == 0, "Unexpected failure to set a list of dns servers: %s", ares_strerror(err)); + } + } + + void StopAres() noexcept { + // Destroy the ares channel + ares_destroy(AresChannel); + AresChannel = nullptr; + } + + private: + STRICT_STFUNC(StateWork, { + hFunc(TEvents::TEvPoison, Handle); + hFunc(TEvDns::TEvGetHostByName, Handle); + hFunc(TEvDns::TEvGetAddr, Handle); + }) + + void Handle(TEvents::TEvPoison::TPtr&) { + Y_VERIFY(!Stopped); + + PushCallback([this] { + // Cancel all current ares requests (will send notifications) + ares_cancel(AresChannel); + + // Mark as stopped last + Stopped = true; + }); + + WorkerThread.Join(); + PassAway(); + } + + private: + enum class ERequestType { + GetHostByName, + GetAddr, + }; + + struct TRequestContext : public TThrRefBase { + using TPtr = TIntrusivePtr<TRequestContext>; + + TThis* Self; + TActorSystem* ActorSystem; + TActorId SelfId; + TActorId Sender; + ui64 Cookie; + ERequestType Type; + + TRequestContext(TThis* self, TActorSystem* as, TActorId selfId, TActorId sender, ui64 cookie, ERequestType type) + : Self(self) + , ActorSystem(as) + , SelfId(selfId) + , Sender(sender) + , Cookie(cookie) + , Type(type) + { } + }; + + private: + void Handle(TEvDns::TEvGetHostByName::TPtr& ev) { + auto* msg = ev->Get(); + auto reqCtx = MakeIntrusive<TRequestContext>( + this, TActivationContext::ActorSystem(), SelfId(), ev->Sender, ev->Cookie, ERequestType::GetHostByName); + PushCallback([this, reqCtx = std::move(reqCtx), name = std::move(msg->Name), family = msg->Family] () mutable { + StartGetHostByName(std::move(reqCtx), std::move(name), family); + }); + } + + void Handle(TEvDns::TEvGetAddr::TPtr& ev) { + auto* msg = ev->Get(); + auto reqCtx = MakeIntrusive<TRequestContext>( + this, TActivationContext::ActorSystem(), SelfId(), ev->Sender, ev->Cookie, ERequestType::GetAddr); + PushCallback([this, reqCtx = std::move(reqCtx), name = std::move(msg->Name), family = msg->Family] () mutable { + StartGetHostByName(std::move(reqCtx), std::move(name), family); + }); + } + + void StartGetHostByName(TRequestContext::TPtr reqCtx, TString name, int family) noexcept { + reqCtx->Ref(); + ares_gethostbyname(AresChannel, name.c_str(), family, + &TThis::GetHostByNameAresCallback, reqCtx.Get()); + } + + private: + static void GetHostByNameAresCallback(void* arg, int status, int timeouts, struct hostent* info) { + Y_UNUSED(timeouts); + TRequestContext::TPtr reqCtx(static_cast<TRequestContext*>(arg)); + reqCtx->UnRef(); + + if (reqCtx->Self->Stopped) { + // Don't send any replies after destruction + return; + } + + switch (reqCtx->Type) { + case ERequestType::GetHostByName: { + auto result = MakeHolder<TEvDns::TEvGetHostByNameResult>(); + if (status == 0) { + switch (info->h_addrtype) { + case AF_INET: { + for (int i = 0; info->h_addr_list[i] != nullptr; ++i) { + result->AddrsV4.emplace_back(*(struct in_addr*)(info->h_addr_list[i])); + } + break; + } + case AF_INET6: { + for (int i = 0; info->h_addr_list[i] != nullptr; ++i) { + result->AddrsV6.emplace_back(*(struct in6_addr*)(info->h_addr_list[i])); + } + break; + } + default: + Y_FAIL("unknown address family in ares callback"); + } + } else { + result->ErrorText = ares_strerror(status); + } + result->Status = status; + + reqCtx->ActorSystem->Send(new IEventHandle(reqCtx->Sender, reqCtx->SelfId, result.Release(), 0, reqCtx->Cookie)); + break; + } + + case ERequestType::GetAddr: { + auto result = MakeHolder<TEvDns::TEvGetAddrResult>(); + if (status == 0 && Y_UNLIKELY(info->h_addr_list[0] == nullptr)) { + status = ARES_ENODATA; + } + if (status == 0) { + switch (info->h_addrtype) { + case AF_INET: { + result->Addr = *(struct in_addr*)(info->h_addr_list[0]); + break; + } + case AF_INET6: { + result->Addr = *(struct in6_addr*)(info->h_addr_list[0]); + break; + } + default: + Y_FAIL("unknown address family in ares callback"); + } + } else { + result->ErrorText = ares_strerror(status); + } + result->Status = status; + + reqCtx->ActorSystem->Send(new IEventHandle(reqCtx->Sender, reqCtx->SelfId, result.Release(), 0, reqCtx->Cookie)); + break; + } + } + } + + private: + static void SockStateCallback(void* data, ares_socket_t socket_fd, int readable, int writable) { + static_cast<TThis*>(data)->DoSockStateCallback(socket_fd, readable, writable); + } + + void DoSockStateCallback(ares_socket_t socket_fd, int readable, int writable) noexcept { + int events = (readable ? (POLLRDNORM | POLLIN) : 0) | (writable ? (POLLWRNORM | POLLOUT) : 0); + if (events == 0) { + AresSockStates.erase(socket_fd); + } else { + AresSockStates[socket_fd].NeededEvents = events; + } + } + + private: + static void* WorkerThreadStart(void* arg) noexcept { + static_cast<TSimpleDnsResolver*>(arg)->WorkerThreadLoop(); + return nullptr; + } + + void WorkerThreadLoop() noexcept { + TThread::SetCurrentThreadName("DnsResolver"); + + TVector<struct pollfd> fds; + while (!Stopped) { + fds.clear(); + fds.reserve(1 + AresSockStates.size()); + { + auto& entry = fds.emplace_back(); + entry.fd = WaitSock(); + entry.events = POLLRDNORM | POLLIN; + } + for (auto& kv : AresSockStates) { + auto& entry = fds.emplace_back(); + entry.fd = kv.first; + entry.events = kv.second.NeededEvents; + } + + int timeout = -1; + struct timeval tv; + if (ares_timeout(AresChannel, nullptr, &tv)) { + timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; + } + + int ret = poll(fds.data(), fds.size(), timeout); + if (ret == -1) { + if (errno == EINTR) { + continue; + } + // we cannot handle failures, run callbacks and pretend everything is ok + RunCallbacks(); + if (Stopped) { + break; + } + ret = 0; + } + + bool ares_called = false; + if (ret > 0) { + for (size_t i = 0; i < fds.size(); ++i) { + auto& entry = fds[i]; + + // Handle WaitSock activation and run callbacks + if (i == 0) { + if (entry.revents & (POLLRDNORM | POLLIN)) { + RunCallbacks(); + if (Stopped) { + break; + } + } + continue; + } + + // All other sockets belong to ares + if (entry.revents == 0) { + continue; + } + // Previous invocation of aress_process_fd might have removed some sockets + if (Y_UNLIKELY(!AresSockStates.contains(entry.fd))) { + continue; + } + ares_process_fd( + AresChannel, + entry.revents & (POLLRDNORM | POLLIN) ? entry.fd : ARES_SOCKET_BAD, + entry.revents & (POLLWRNORM | POLLOUT) ? entry.fd : ARES_SOCKET_BAD); + ares_called = true; + } + + if (Stopped) { + break; + } + } + + if (!ares_called) { + // Let ares handle timeouts + ares_process_fd(AresChannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + } + } + } + + private: + struct TSockState { + short NeededEvents = 0; // poll events + }; + + private: + TSimpleDnsResolverOptions Options; + TThread WorkerThread; + + ares_channel AresChannel; + THashMap<SOCKET, TSockState> AresSockStates; + + bool Stopped = false; + }; + + IActor* CreateSimpleDnsResolver(TSimpleDnsResolverOptions options) { + return new TSimpleDnsResolver(std::move(options)); + } + +} // namespace NDnsResolver +} // namespace NActors diff --git a/library/cpp/actors/dnsresolver/dnsresolver.h b/library/cpp/actors/dnsresolver/dnsresolver.h index 88fc74df7d..6aa28641fc 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver.h +++ b/library/cpp/actors/dnsresolver/dnsresolver.h @@ -1,128 +1,128 @@ -#pragma once - -#include <library/cpp/actors/core/actor.h> -#include <library/cpp/actors/core/events.h> -#include <library/cpp/actors/core/event_local.h> -#include <library/cpp/monlib/dynamic_counters/counters.h> -#include <util/network/address.h> -#include <variant> - -namespace NActors { -namespace NDnsResolver { - - struct TEvDns { - enum EEv { - EvGetHostByName = EventSpaceBegin(TEvents::ES_DNS), - EvGetHostByNameResult, - EvGetAddr, - EvGetAddrResult, - }; - - /** - * TEvGetHostByName returns the result of ares_gethostbyname - */ - struct TEvGetHostByName : public TEventLocal<TEvGetHostByName, EvGetHostByName> { - TString Name; - int Family; - - explicit TEvGetHostByName(TString name, int family = AF_UNSPEC) - : Name(std::move(name)) - , Family(family) - { } - }; - - struct TEvGetHostByNameResult : public TEventLocal<TEvGetHostByNameResult, EvGetHostByNameResult> { - TVector<struct in_addr> AddrsV4; - TVector<struct in6_addr> AddrsV6; - TString ErrorText; - int Status = 0; - }; - - /** - * TEvGetAddr returns a single address for a given hostname - */ - struct TEvGetAddr : public TEventLocal<TEvGetAddr, EvGetAddr> { - TString Name; - int Family; - - explicit TEvGetAddr(TString name, int family = AF_UNSPEC) - : Name(std::move(name)) - , Family(family) - { } - }; - - struct TEvGetAddrResult : public TEventLocal<TEvGetAddrResult, EvGetAddrResult> { - // N.B. "using" here doesn't work with Visual Studio compiler - typedef struct in6_addr TIPv6Addr; - typedef struct in_addr TIPv4Addr; - - std::variant<std::monostate, TIPv6Addr, TIPv4Addr> Addr; - TString ErrorText; - int Status = 0; - - bool IsV6() const { - return std::holds_alternative<TIPv6Addr>(Addr); - } - - bool IsV4() const { - return std::holds_alternative<TIPv4Addr>(Addr); - } - - const TIPv6Addr& GetAddrV6() const { - const TIPv6Addr* p = std::get_if<TIPv6Addr>(&Addr); - Y_VERIFY(p, "Result is not an ipv6 address"); - return *p; - } - - const TIPv4Addr& GetAddrV4() const { - const TIPv4Addr* p = std::get_if<TIPv4Addr>(&Addr); - Y_VERIFY(p, "Result is not an ipv4 address"); - return *p; - } - }; - }; - - struct TSimpleDnsResolverOptions { - // Initial per-server timeout, grows exponentially with each retry - TDuration Timeout = TDuration::Seconds(1); - // Number of attempts per-server - int Attempts = 2; - // Optional list of custom dns servers (ip.v4[:port], ip::v6 or [ip::v6]:port format) - TVector<TString> Servers; - }; - - IActor* CreateSimpleDnsResolver(TSimpleDnsResolverOptions options = TSimpleDnsResolverOptions()); - - struct TCachingDnsResolverOptions { - // Soft expire time specifies delay before name is refreshed in background - TDuration SoftNegativeExpireTime = TDuration::Seconds(1); - TDuration SoftPositiveExpireTime = TDuration::Seconds(10); - // Hard expire time specifies delay before the last result is forgotten - TDuration HardNegativeExpireTime = TDuration::Seconds(10); - TDuration HardPositiveExpireTime = TDuration::Hours(2); - // Allow these request families - bool AllowIPv6 = true; - bool AllowIPv4 = true; - // Optional counters - NMonitoring::TDynamicCounterPtr MonCounters = nullptr; - }; - - IActor* CreateCachingDnsResolver(TActorId upstream, TCachingDnsResolverOptions options = TCachingDnsResolverOptions()); - - struct TOnDemandDnsResolverOptions - : public TSimpleDnsResolverOptions - , public TCachingDnsResolverOptions - { - }; - - IActor* CreateOnDemandDnsResolver(TOnDemandDnsResolverOptions options = TOnDemandDnsResolverOptions()); - - /** - * Returns actor id of a globally registered dns resolver - */ - inline TActorId MakeDnsResolverActorId() { - return TActorId(0, TStringBuf("dnsresolver")); - } - -} // namespace NDnsResolver -} // namespace NActors +#pragma once + +#include <library/cpp/actors/core/actor.h> +#include <library/cpp/actors/core/events.h> +#include <library/cpp/actors/core/event_local.h> +#include <library/cpp/monlib/dynamic_counters/counters.h> +#include <util/network/address.h> +#include <variant> + +namespace NActors { +namespace NDnsResolver { + + struct TEvDns { + enum EEv { + EvGetHostByName = EventSpaceBegin(TEvents::ES_DNS), + EvGetHostByNameResult, + EvGetAddr, + EvGetAddrResult, + }; + + /** + * TEvGetHostByName returns the result of ares_gethostbyname + */ + struct TEvGetHostByName : public TEventLocal<TEvGetHostByName, EvGetHostByName> { + TString Name; + int Family; + + explicit TEvGetHostByName(TString name, int family = AF_UNSPEC) + : Name(std::move(name)) + , Family(family) + { } + }; + + struct TEvGetHostByNameResult : public TEventLocal<TEvGetHostByNameResult, EvGetHostByNameResult> { + TVector<struct in_addr> AddrsV4; + TVector<struct in6_addr> AddrsV6; + TString ErrorText; + int Status = 0; + }; + + /** + * TEvGetAddr returns a single address for a given hostname + */ + struct TEvGetAddr : public TEventLocal<TEvGetAddr, EvGetAddr> { + TString Name; + int Family; + + explicit TEvGetAddr(TString name, int family = AF_UNSPEC) + : Name(std::move(name)) + , Family(family) + { } + }; + + struct TEvGetAddrResult : public TEventLocal<TEvGetAddrResult, EvGetAddrResult> { + // N.B. "using" here doesn't work with Visual Studio compiler + typedef struct in6_addr TIPv6Addr; + typedef struct in_addr TIPv4Addr; + + std::variant<std::monostate, TIPv6Addr, TIPv4Addr> Addr; + TString ErrorText; + int Status = 0; + + bool IsV6() const { + return std::holds_alternative<TIPv6Addr>(Addr); + } + + bool IsV4() const { + return std::holds_alternative<TIPv4Addr>(Addr); + } + + const TIPv6Addr& GetAddrV6() const { + const TIPv6Addr* p = std::get_if<TIPv6Addr>(&Addr); + Y_VERIFY(p, "Result is not an ipv6 address"); + return *p; + } + + const TIPv4Addr& GetAddrV4() const { + const TIPv4Addr* p = std::get_if<TIPv4Addr>(&Addr); + Y_VERIFY(p, "Result is not an ipv4 address"); + return *p; + } + }; + }; + + struct TSimpleDnsResolverOptions { + // Initial per-server timeout, grows exponentially with each retry + TDuration Timeout = TDuration::Seconds(1); + // Number of attempts per-server + int Attempts = 2; + // Optional list of custom dns servers (ip.v4[:port], ip::v6 or [ip::v6]:port format) + TVector<TString> Servers; + }; + + IActor* CreateSimpleDnsResolver(TSimpleDnsResolverOptions options = TSimpleDnsResolverOptions()); + + struct TCachingDnsResolverOptions { + // Soft expire time specifies delay before name is refreshed in background + TDuration SoftNegativeExpireTime = TDuration::Seconds(1); + TDuration SoftPositiveExpireTime = TDuration::Seconds(10); + // Hard expire time specifies delay before the last result is forgotten + TDuration HardNegativeExpireTime = TDuration::Seconds(10); + TDuration HardPositiveExpireTime = TDuration::Hours(2); + // Allow these request families + bool AllowIPv6 = true; + bool AllowIPv4 = true; + // Optional counters + NMonitoring::TDynamicCounterPtr MonCounters = nullptr; + }; + + IActor* CreateCachingDnsResolver(TActorId upstream, TCachingDnsResolverOptions options = TCachingDnsResolverOptions()); + + struct TOnDemandDnsResolverOptions + : public TSimpleDnsResolverOptions + , public TCachingDnsResolverOptions + { + }; + + IActor* CreateOnDemandDnsResolver(TOnDemandDnsResolverOptions options = TOnDemandDnsResolverOptions()); + + /** + * Returns actor id of a globally registered dns resolver + */ + inline TActorId MakeDnsResolverActorId() { + return TActorId(0, TStringBuf("dnsresolver")); + } + +} // namespace NDnsResolver +} // namespace NActors diff --git a/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp b/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp index 02760f4c27..a1688854c4 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp +++ b/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp @@ -1,730 +1,730 @@ -#include "dnsresolver.h" - -#include <library/cpp/actors/core/hfunc.h> -#include <util/generic/intrlist.h> - -#include <ares.h> - -#include <queue> - -namespace NActors { -namespace NDnsResolver { - - class TCachingDnsResolver : public TActor<TCachingDnsResolver> { - public: - struct TMonCounters { - NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightV4; - NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightV6; - NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsV4; - NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsV6; - NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalV4; - NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalV6; - - NMonitoring::TDynamicCounters::TCounterPtr IncomingInFlight; - NMonitoring::TDynamicCounters::TCounterPtr IncomingErrors; - NMonitoring::TDynamicCounters::TCounterPtr IncomingTotal; - - NMonitoring::TDynamicCounters::TCounterPtr CacheSize; - NMonitoring::TDynamicCounters::TCounterPtr CacheHits; - NMonitoring::TDynamicCounters::TCounterPtr CacheMisses; - - TMonCounters(const NMonitoring::TDynamicCounterPtr& counters) - : OutgoingInFlightV4(counters->GetCounter("DnsResolver/Outgoing/InFlight/V4", false)) - , OutgoingInFlightV6(counters->GetCounter("DnsResolver/Outgoing/InFlight/V6", false)) - , OutgoingErrorsV4(counters->GetCounter("DnsResolver/Outgoing/Errors/V4", true)) - , OutgoingErrorsV6(counters->GetCounter("DnsResolver/Outgoing/Errors/V6", true)) - , OutgoingTotalV4(counters->GetCounter("DnsResolver/Outgoing/Total/V4", true)) - , OutgoingTotalV6(counters->GetCounter("DnsResolver/Outgoing/Total/V6", true)) - , IncomingInFlight(counters->GetCounter("DnsResolver/Incoming/InFlight", false)) - , IncomingErrors(counters->GetCounter("DnsResolver/Incoming/Errors", true)) - , IncomingTotal(counters->GetCounter("DnsResolver/Incoming/Total", true)) - , CacheSize(counters->GetCounter("DnsResolver/Cache/Size", false)) - , CacheHits(counters->GetCounter("DnsResolver/Cache/Hits", true)) - , CacheMisses(counters->GetCounter("DnsResolver/Cache/Misses", true)) - { } - }; - - public: - TCachingDnsResolver(TActorId upstream, TCachingDnsResolverOptions options) - : TActor(&TThis::StateWork) - , Upstream(upstream) - , Options(std::move(options)) - , MonCounters(Options.MonCounters ? new TMonCounters(Options.MonCounters) : nullptr) - { } - - static constexpr EActivityType ActorActivityType() { - return DNS_RESOLVER; - } - - private: - STRICT_STFUNC(StateWork, { - hFunc(TEvents::TEvPoison, Handle); - hFunc(TEvDns::TEvGetHostByName, Handle); - hFunc(TEvDns::TEvGetAddr, Handle); - hFunc(TEvDns::TEvGetHostByNameResult, Handle); - hFunc(TEvents::TEvUndelivered, Handle); - }); - - void Handle(TEvents::TEvPoison::TPtr&) { - DropPending(ARES_ECANCELLED); - PassAway(); - } - - void Handle(TEvDns::TEvGetHostByName::TPtr& ev) { - auto req = MakeHolder<TIncomingRequest>(); - req->Type = EIncomingRequestType::GetHostByName; - req->Sender = ev->Sender; - req->Cookie = ev->Cookie; - req->Name = std::move(ev->Get()->Name); - req->Family = ev->Get()->Family; - EnqueueRequest(std::move(req)); - } - - void Handle(TEvDns::TEvGetAddr::TPtr& ev) { - auto req = MakeHolder<TIncomingRequest>(); - req->Type = EIncomingRequestType::GetAddr; - req->Sender = ev->Sender; - req->Cookie = ev->Cookie; - req->Name = std::move(ev->Get()->Name); - req->Family = ev->Get()->Family; - EnqueueRequest(std::move(req)); - } - - void Handle(TEvDns::TEvGetHostByNameResult::TPtr& ev) { - auto waitingIt = WaitingRequests.find(ev->Cookie); - Y_VERIFY(waitingIt != WaitingRequests.end(), "Unexpected reply, reqId=%" PRIu64, ev->Cookie); - auto waitingInfo = waitingIt->second; - WaitingRequests.erase(waitingIt); - - switch (waitingInfo.Family) { - case AF_INET6: - if (ev->Get()->Status) { - ProcessErrorV6(waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText)); - } else { - ProcessAddrsV6(waitingInfo.Position, std::move(ev->Get()->AddrsV6)); - } - break; - - case AF_INET: - if (ev->Get()->Status) { - ProcessErrorV4(waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText)); - } else { - ProcessAddrsV4(waitingInfo.Position, std::move(ev->Get()->AddrsV4)); - } - break; - - default: - Y_FAIL("Unexpected request family %d", waitingInfo.Family); - } - } - - void Handle(TEvents::TEvUndelivered::TPtr& ev) { - switch (ev->Get()->SourceType) { - case TEvDns::TEvGetHostByName::EventType: { - auto waitingIt = WaitingRequests.find(ev->Cookie); - Y_VERIFY(waitingIt != WaitingRequests.end(), "Unexpected TEvUndelivered, reqId=%" PRIu64, ev->Cookie); - auto waitingInfo = waitingIt->second; - WaitingRequests.erase(waitingIt); - - switch (waitingInfo.Family) { - case AF_INET6: - ProcessErrorV6(waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver"); - break; - case AF_INET: - ProcessErrorV4(waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver"); - break; - default: - Y_FAIL("Unexpected request family %d", waitingInfo.Family); - } - - break; - } - - default: - Y_FAIL("Unexpected TEvUndelievered, type=%" PRIu32, ev->Get()->SourceType); - } - } - - private: - enum EIncomingRequestType { - GetHostByName, - GetAddr, - }; - - struct TIncomingRequest : public TIntrusiveListItem<TIncomingRequest> { - EIncomingRequestType Type; - TActorId Sender; - ui64 Cookie; - TString Name; - int Family; - }; - - using TIncomingRequestList = TIntrusiveListWithAutoDelete<TIncomingRequest, TDelete>; - - void EnqueueRequest(THolder<TIncomingRequest> req) { - if (MonCounters) { - ++*MonCounters->IncomingTotal; - } - - CleanupExpired(TActivationContext::Now()); - - switch (req->Family) { - case AF_UNSPEC: - if (Options.AllowIPv6) { - EnqueueRequestIPv6(std::move(req)); - return; - } - if (Options.AllowIPv4) { - EnqueueRequestIPv4(std::move(req)); - return; - } - break; - - case AF_INET6: - if (Options.AllowIPv6) { - EnqueueRequestIPv6(std::move(req)); - return; - } - break; - - case AF_INET: - if (Options.AllowIPv4) { - EnqueueRequestIPv4(std::move(req)); - return; - } - break; - } - - ReplyWithError(std::move(req), ARES_EBADFAMILY); - } - - void EnqueueRequestIPv6(THolder<TIncomingRequest> req) { - auto now = TActivationContext::Now(); - - auto& fullState = NameToState[req->Name]; - if (MonCounters) { - *MonCounters->CacheSize = NameToState.size(); - } - - auto& state = fullState.StateIPv6; - EnsureRequest(state, req->Name, AF_INET6, now); - - if (state.IsHardExpired(now)) { - Y_VERIFY(state.Waiting); - if (MonCounters) { - ++*MonCounters->CacheMisses; - } - // We need to wait for ipv6 reply, schedule ipv4 request in parallel if needed - if (Options.AllowIPv4) { - EnsureRequest(fullState.StateIPv4, req->Name, AF_INET, now); - } - state.WaitingRequests.PushBack(req.Release()); - return; - } - - // We want to retry AF_UNSPEC with IPv4 in some cases - if (req->Family == AF_UNSPEC && Options.AllowIPv4 && state.RetryUnspec()) { - EnqueueRequestIPv4(std::move(req)); - return; - } - - if (MonCounters) { - ++*MonCounters->CacheHits; - } - - if (state.Status != 0) { - ReplyWithError(std::move(req), state.Status, state.ErrorText); - } else { - ReplyWithAddrs(std::move(req), fullState.AddrsIPv6); - } - } - - void EnqueueRequestIPv4(THolder<TIncomingRequest> req, bool isCacheMiss = false) { - auto now = TActivationContext::Now(); - - auto& fullState = NameToState[req->Name]; - if (MonCounters) { - *MonCounters->CacheSize = NameToState.size(); - } - - auto& state = fullState.StateIPv4; - EnsureRequest(state, req->Name, AF_INET, now); - - if (state.IsHardExpired(now)) { - Y_VERIFY(state.Waiting); - if (MonCounters && !isCacheMiss) { - ++*MonCounters->CacheMisses; - } - state.WaitingRequests.PushBack(req.Release()); - return; - } - - if (MonCounters && !isCacheMiss) { - ++*MonCounters->CacheHits; - } - - if (state.Status != 0) { - ReplyWithError(std::move(req), state.Status, state.ErrorText); - } else { - ReplyWithAddrs(std::move(req), fullState.AddrsIPv4); - } - } - - private: - struct TFamilyState { - TIncomingRequestList WaitingRequests; - TInstant SoftDeadline; - TInstant HardDeadline; - TInstant NextSoftDeadline; - TInstant NextHardDeadline; - TString ErrorText; - int Status = -1; // never requested before - bool InSoftHeap = false; - bool InHardHeap = false; - bool Waiting = false; - - bool Needed() const { - return InSoftHeap || InHardHeap || Waiting; - } - - bool RetryUnspec() const { - return ( - Status == ARES_ENODATA || - Status == ARES_EBADRESP || - Status == ARES_ETIMEOUT); - } - - bool ServerReplied() const { - return ServerReplied(Status); - } - - bool IsSoftExpired(TInstant now) const { - return !InSoftHeap || NextSoftDeadline < now; - } - - bool IsHardExpired(TInstant now) const { - return !InHardHeap || NextHardDeadline < now; - } - - static bool ServerReplied(int status) { - return ( - status == ARES_SUCCESS || - status == ARES_ENODATA || - status == ARES_ENOTFOUND); - } - }; - - struct TState { - TFamilyState StateIPv6; - TFamilyState StateIPv4; - TVector<struct in6_addr> AddrsIPv6; - TVector<struct in_addr> AddrsIPv4; - - bool Needed() const { - return StateIPv6.Needed() || StateIPv4.Needed(); - } - }; - - using TNameToState = THashMap<TString, TState>; - - template<const TFamilyState TState::* StateToFamily, - const TInstant TFamilyState::* FamilyToDeadline> - struct THeapCompare { - // returns true when b < a - bool operator()(TNameToState::iterator a, TNameToState::iterator b) const { - const TState& aState = a->second; - const TState& bState = b->second; - const TFamilyState& aFamily = aState.*StateToFamily; - const TFamilyState& bFamily = bState.*StateToFamily; - const TInstant& aDeadline = aFamily.*FamilyToDeadline; - const TInstant& bDeadline = bFamily.*FamilyToDeadline; - return bDeadline < aDeadline; - } - }; - - template<const TFamilyState TState::* StateToFamily, - const TInstant TFamilyState::* FamilyToDeadline> - using TStateHeap = std::priority_queue< - TNameToState::iterator, - std::vector<TNameToState::iterator>, - THeapCompare<StateToFamily, FamilyToDeadline> - >; - - struct TWaitingInfo { - TNameToState::iterator Position; - int Family; - }; - - private: - void EnsureRequest(TFamilyState& state, const TString& name, int family, TInstant now) { - if (state.Waiting) { - return; // request is already pending - } - - if (!state.IsSoftExpired(now) && !state.IsHardExpired(now)) { - return; // response is not expired yet - } - - if (MonCounters) { - switch (family) { - case AF_INET6: - ++*MonCounters->OutgoingInFlightV6; - ++*MonCounters->OutgoingTotalV6; - break; - case AF_INET: - ++*MonCounters->OutgoingInFlightV4; - ++*MonCounters->OutgoingTotalV4; - break; - } - } - - ui64 reqId = ++LastRequestId; - auto& req = WaitingRequests[reqId]; - req.Position = NameToState.find(name); - req.Family = family; - Y_VERIFY(req.Position != NameToState.end()); - - Send(Upstream, new TEvDns::TEvGetHostByName(name, family), IEventHandle::FlagTrackDelivery, reqId); - state.Waiting = true; - } - - template<TFamilyState TState::* StateToFamily, - TInstant TFamilyState::* FamilyToDeadline, - TInstant TFamilyState::* FamilyToNextDeadline, - bool TFamilyState::* FamilyToFlag, - class THeap> - void PushToHeap(THeap& heap, TNameToState::iterator it, TInstant newDeadline) { - auto& family = it->second.*StateToFamily; - TInstant& deadline = family.*FamilyToDeadline; - TInstant& nextDeadline = family.*FamilyToNextDeadline; - bool& flag = family.*FamilyToFlag; - nextDeadline = newDeadline; - if (!flag) { - deadline = newDeadline; - heap.push(it); - flag = true; - } - } - - void PushSoftV6(TNameToState::iterator it, TInstant newDeadline) { - PushToHeap<&TState::StateIPv6, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv6, it, newDeadline); - } - - void PushHardV6(TNameToState::iterator it, TInstant newDeadline) { - PushToHeap<&TState::StateIPv6, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv6, it, newDeadline); - } - - void PushSoftV4(TNameToState::iterator it, TInstant newDeadline) { - PushToHeap<&TState::StateIPv4, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv4, it, newDeadline); - } - - void PushHardV4(TNameToState::iterator it, TInstant newDeadline) { - PushToHeap<&TState::StateIPv4, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv4, it, newDeadline); - } - - void ProcessErrorV6(TNameToState::iterator it, int status, TString errorText) { - auto now = TActivationContext::Now(); - if (MonCounters) { - --*MonCounters->OutgoingInFlightV6; - ++*MonCounters->OutgoingErrorsV6; - } - - auto& state = it->second.StateIPv6; - Y_VERIFY(state.Waiting, "Got error for a state we are not waiting"); - state.Waiting = false; - - // When we have a cached positive reply, don't overwrite it with spurious errors - const bool serverReplied = TFamilyState::ServerReplied(status); - if (!serverReplied && state.ServerReplied() && !state.IsHardExpired(now)) { - PushSoftV6(it, now + Options.SoftNegativeExpireTime); - if (state.Status == ARES_SUCCESS) { - SendAddrsV6(it); - } else { - SendErrorsV6(it, now); - } - return; - } - - state.Status = status; - state.ErrorText = std::move(errorText); - PushSoftV6(it, now + Options.SoftNegativeExpireTime); - if (serverReplied) { - // Server actually replied, so keep it cached for longer - PushHardV6(it, now + Options.HardPositiveExpireTime); - } else { - PushHardV6(it, now + Options.HardNegativeExpireTime); - } - - SendErrorsV6(it, now); - } - - void SendErrorsV6(TNameToState::iterator it, TInstant now) { - bool cleaned = false; - auto& state = it->second.StateIPv6; - while (state.WaitingRequests) { - THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); - if (req->Family == AF_UNSPEC && Options.AllowIPv4 && state.RetryUnspec()) { - if (!cleaned) { - CleanupExpired(now); - cleaned = true; - } - EnqueueRequestIPv4(std::move(req), /* isCacheMiss */ true); - } else { - ReplyWithError(std::move(req), state.Status, state.ErrorText); - } - } - } - - void ProcessErrorV4(TNameToState::iterator it, int status, TString errorText) { - auto now = TActivationContext::Now(); - if (MonCounters) { - --*MonCounters->OutgoingInFlightV4; - ++*MonCounters->OutgoingErrorsV4; - } - - auto& state = it->second.StateIPv4; - Y_VERIFY(state.Waiting, "Got error for a state we are not waiting"); - state.Waiting = false; - - // When we have a cached positive reply, don't overwrite it with spurious errors - const bool serverReplied = TFamilyState::ServerReplied(status); - if (!serverReplied && state.ServerReplied() && !state.IsHardExpired(now)) { - PushSoftV4(it, now + Options.SoftNegativeExpireTime); - if (state.Status == ARES_SUCCESS) { - SendAddrsV4(it); - } else { - SendErrorsV4(it); - } - return; - } - - state.Status = status; - state.ErrorText = std::move(errorText); - PushSoftV4(it, now + Options.SoftNegativeExpireTime); - if (serverReplied) { - // Server actually replied, so keep it cached for longer - PushHardV4(it, now + Options.HardPositiveExpireTime); - } else { - PushHardV4(it, now + Options.HardNegativeExpireTime); - } - - SendErrorsV4(it); - } - - void SendErrorsV4(TNameToState::iterator it) { - auto& state = it->second.StateIPv4; - while (state.WaitingRequests) { - THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); - ReplyWithError(std::move(req), state.Status, state.ErrorText); - } - } - - void ProcessAddrsV6(TNameToState::iterator it, TVector<struct in6_addr> addrs) { - if (Y_UNLIKELY(addrs.empty())) { - // Probably unnecessary: we don't want to deal with empty address lists - return ProcessErrorV6(it, ARES_ENODATA, ares_strerror(ARES_ENODATA)); - } - - auto now = TActivationContext::Now(); - if (MonCounters) { - --*MonCounters->OutgoingInFlightV6; - } - - auto& state = it->second.StateIPv6; - Y_VERIFY(state.Waiting, "Got reply for a state we are not waiting"); - state.Waiting = false; - - state.Status = ARES_SUCCESS; - it->second.AddrsIPv6 = std::move(addrs); - PushSoftV6(it, now + Options.SoftPositiveExpireTime); - PushHardV6(it, now + Options.HardPositiveExpireTime); - - SendAddrsV6(it); - } - - void SendAddrsV6(TNameToState::iterator it) { - auto& state = it->second.StateIPv6; - while (state.WaitingRequests) { - THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); - ReplyWithAddrs(std::move(req), it->second.AddrsIPv6); - } - } - - void ProcessAddrsV4(TNameToState::iterator it, TVector<struct in_addr> addrs) { - if (Y_UNLIKELY(addrs.empty())) { - // Probably unnecessary: we don't want to deal with empty address lists - return ProcessErrorV4(it, ARES_ENODATA, ares_strerror(ARES_ENODATA)); - } - - auto now = TActivationContext::Now(); - if (MonCounters) { - --*MonCounters->OutgoingInFlightV4; - } - - auto& state = it->second.StateIPv4; - Y_VERIFY(state.Waiting, "Got reply for a state we are not waiting"); - state.Waiting = false; - - state.Status = ARES_SUCCESS; - it->second.AddrsIPv4 = std::move(addrs); - PushSoftV4(it, now + Options.SoftPositiveExpireTime); - PushHardV4(it, now + Options.HardPositiveExpireTime); - - SendAddrsV4(it); - } - - void SendAddrsV4(TNameToState::iterator it) { - auto& state = it->second.StateIPv4; - while (state.WaitingRequests) { - THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); - ReplyWithAddrs(std::move(req), it->second.AddrsIPv4); - } - } - - private: - template<TFamilyState TState::*StateToFamily, - TInstant TFamilyState::* FamilyToDeadline, - TInstant TFamilyState::* FamilyToNextDeadline, - bool TFamilyState::* FamilyToFlag> - void DoCleanupExpired(TStateHeap<StateToFamily, FamilyToDeadline>& heap, TInstant now) { - while (!heap.empty()) { - auto it = heap.top(); - auto& family = it->second.*StateToFamily; - TInstant& deadline = family.*FamilyToDeadline; - if (now <= deadline) { - break; - } - - bool& flag = family.*FamilyToFlag; - Y_VERIFY(flag); - heap.pop(); - flag = false; - - TInstant& nextDeadline = family.*FamilyToNextDeadline; - if (now < nextDeadline) { - deadline = nextDeadline; - heap.push(it); - flag = true; - continue; - } - - // Remove unnecessary items - if (!it->second.Needed()) { - NameToState.erase(it); - if (MonCounters) { - *MonCounters->CacheSize = NameToState.size(); - } - } - } - } - - void CleanupExpired(TInstant now) { - DoCleanupExpired<&TState::StateIPv6, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv6, now); - DoCleanupExpired<&TState::StateIPv6, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv6, now); - DoCleanupExpired<&TState::StateIPv4, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv4, now); - DoCleanupExpired<&TState::StateIPv4, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv4, now); - } - - template<class TEvent> - void SendError(TActorId replyTo, ui64 cookie, int status, const TString& errorText) { - auto reply = MakeHolder<TEvent>(); - reply->Status = status; - reply->ErrorText = errorText; - this->Send(replyTo, reply.Release(), 0, cookie); - } - - void ReplyWithError(THolder<TIncomingRequest> req, int status, const TString& errorText) { - if (MonCounters) { - ++*MonCounters->IncomingErrors; - } - switch (req->Type) { - case EIncomingRequestType::GetHostByName: { - SendError<TEvDns::TEvGetHostByNameResult>(req->Sender, req->Cookie, status, errorText); - break; - } - case EIncomingRequestType::GetAddr: { - SendError<TEvDns::TEvGetAddrResult>(req->Sender, req->Cookie, status, errorText); - break; - } - } - } - - void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in6_addr>& addrs) { - switch (req->Type) { - case EIncomingRequestType::GetHostByName: { - auto reply = MakeHolder<TEvDns::TEvGetHostByNameResult>(); - reply->AddrsV6 = addrs; - Send(req->Sender, reply.Release(), 0, req->Cookie); - break; - } - case EIncomingRequestType::GetAddr: { - Y_VERIFY(!addrs.empty()); - auto reply = MakeHolder<TEvDns::TEvGetAddrResult>(); - reply->Addr = addrs.front(); - Send(req->Sender, reply.Release(), 0, req->Cookie); - break; - } - } - } - - void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in_addr>& addrs) { - switch (req->Type) { - case EIncomingRequestType::GetHostByName: { - auto reply = MakeHolder<TEvDns::TEvGetHostByNameResult>(); - reply->AddrsV4 = addrs; - Send(req->Sender, reply.Release(), 0, req->Cookie); - break; - } - case EIncomingRequestType::GetAddr: { - Y_VERIFY(!addrs.empty()); - auto reply = MakeHolder<TEvDns::TEvGetAddrResult>(); - reply->Addr = addrs.front(); - Send(req->Sender, reply.Release(), 0, req->Cookie); - break; - } - } - } - - void ReplyWithError(THolder<TIncomingRequest> req, int status) { - ReplyWithError(std::move(req), status, ares_strerror(status)); - } - - void DropPending(TIncomingRequestList& list, int status, const TString& errorText) { - while (list) { - THolder<TIncomingRequest> req(list.PopFront()); - ReplyWithError(std::move(req), status, errorText); - } - } - - void DropPending(int status, const TString& errorText) { - for (auto& [name, state] : NameToState) { - DropPending(state.StateIPv6.WaitingRequests, status, errorText); - DropPending(state.StateIPv4.WaitingRequests, status, errorText); - } - } - - void DropPending(int status) { - DropPending(status, ares_strerror(status)); - } - - private: - const TActorId Upstream; - const TCachingDnsResolverOptions Options; - const THolder<TMonCounters> MonCounters; - - TNameToState NameToState; - TStateHeap<&TState::StateIPv6, &TFamilyState::SoftDeadline> SoftHeapIPv6; - TStateHeap<&TState::StateIPv6, &TFamilyState::HardDeadline> HardHeapIPv6; - TStateHeap<&TState::StateIPv4, &TFamilyState::SoftDeadline> SoftHeapIPv4; - TStateHeap<&TState::StateIPv4, &TFamilyState::HardDeadline> HardHeapIPv4; - - THashMap<ui64, TWaitingInfo> WaitingRequests; - ui64 LastRequestId = 0; - }; - - IActor* CreateCachingDnsResolver(TActorId upstream, TCachingDnsResolverOptions options) { - return new TCachingDnsResolver(upstream, std::move(options)); - } - -} // namespace NDnsResolver -} // namespace NActors +#include "dnsresolver.h" + +#include <library/cpp/actors/core/hfunc.h> +#include <util/generic/intrlist.h> + +#include <ares.h> + +#include <queue> + +namespace NActors { +namespace NDnsResolver { + + class TCachingDnsResolver : public TActor<TCachingDnsResolver> { + public: + struct TMonCounters { + NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightV4; + NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightV6; + NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsV4; + NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsV6; + NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalV4; + NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalV6; + + NMonitoring::TDynamicCounters::TCounterPtr IncomingInFlight; + NMonitoring::TDynamicCounters::TCounterPtr IncomingErrors; + NMonitoring::TDynamicCounters::TCounterPtr IncomingTotal; + + NMonitoring::TDynamicCounters::TCounterPtr CacheSize; + NMonitoring::TDynamicCounters::TCounterPtr CacheHits; + NMonitoring::TDynamicCounters::TCounterPtr CacheMisses; + + TMonCounters(const NMonitoring::TDynamicCounterPtr& counters) + : OutgoingInFlightV4(counters->GetCounter("DnsResolver/Outgoing/InFlight/V4", false)) + , OutgoingInFlightV6(counters->GetCounter("DnsResolver/Outgoing/InFlight/V6", false)) + , OutgoingErrorsV4(counters->GetCounter("DnsResolver/Outgoing/Errors/V4", true)) + , OutgoingErrorsV6(counters->GetCounter("DnsResolver/Outgoing/Errors/V6", true)) + , OutgoingTotalV4(counters->GetCounter("DnsResolver/Outgoing/Total/V4", true)) + , OutgoingTotalV6(counters->GetCounter("DnsResolver/Outgoing/Total/V6", true)) + , IncomingInFlight(counters->GetCounter("DnsResolver/Incoming/InFlight", false)) + , IncomingErrors(counters->GetCounter("DnsResolver/Incoming/Errors", true)) + , IncomingTotal(counters->GetCounter("DnsResolver/Incoming/Total", true)) + , CacheSize(counters->GetCounter("DnsResolver/Cache/Size", false)) + , CacheHits(counters->GetCounter("DnsResolver/Cache/Hits", true)) + , CacheMisses(counters->GetCounter("DnsResolver/Cache/Misses", true)) + { } + }; + + public: + TCachingDnsResolver(TActorId upstream, TCachingDnsResolverOptions options) + : TActor(&TThis::StateWork) + , Upstream(upstream) + , Options(std::move(options)) + , MonCounters(Options.MonCounters ? new TMonCounters(Options.MonCounters) : nullptr) + { } + + static constexpr EActivityType ActorActivityType() { + return DNS_RESOLVER; + } + + private: + STRICT_STFUNC(StateWork, { + hFunc(TEvents::TEvPoison, Handle); + hFunc(TEvDns::TEvGetHostByName, Handle); + hFunc(TEvDns::TEvGetAddr, Handle); + hFunc(TEvDns::TEvGetHostByNameResult, Handle); + hFunc(TEvents::TEvUndelivered, Handle); + }); + + void Handle(TEvents::TEvPoison::TPtr&) { + DropPending(ARES_ECANCELLED); + PassAway(); + } + + void Handle(TEvDns::TEvGetHostByName::TPtr& ev) { + auto req = MakeHolder<TIncomingRequest>(); + req->Type = EIncomingRequestType::GetHostByName; + req->Sender = ev->Sender; + req->Cookie = ev->Cookie; + req->Name = std::move(ev->Get()->Name); + req->Family = ev->Get()->Family; + EnqueueRequest(std::move(req)); + } + + void Handle(TEvDns::TEvGetAddr::TPtr& ev) { + auto req = MakeHolder<TIncomingRequest>(); + req->Type = EIncomingRequestType::GetAddr; + req->Sender = ev->Sender; + req->Cookie = ev->Cookie; + req->Name = std::move(ev->Get()->Name); + req->Family = ev->Get()->Family; + EnqueueRequest(std::move(req)); + } + + void Handle(TEvDns::TEvGetHostByNameResult::TPtr& ev) { + auto waitingIt = WaitingRequests.find(ev->Cookie); + Y_VERIFY(waitingIt != WaitingRequests.end(), "Unexpected reply, reqId=%" PRIu64, ev->Cookie); + auto waitingInfo = waitingIt->second; + WaitingRequests.erase(waitingIt); + + switch (waitingInfo.Family) { + case AF_INET6: + if (ev->Get()->Status) { + ProcessErrorV6(waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText)); + } else { + ProcessAddrsV6(waitingInfo.Position, std::move(ev->Get()->AddrsV6)); + } + break; + + case AF_INET: + if (ev->Get()->Status) { + ProcessErrorV4(waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText)); + } else { + ProcessAddrsV4(waitingInfo.Position, std::move(ev->Get()->AddrsV4)); + } + break; + + default: + Y_FAIL("Unexpected request family %d", waitingInfo.Family); + } + } + + void Handle(TEvents::TEvUndelivered::TPtr& ev) { + switch (ev->Get()->SourceType) { + case TEvDns::TEvGetHostByName::EventType: { + auto waitingIt = WaitingRequests.find(ev->Cookie); + Y_VERIFY(waitingIt != WaitingRequests.end(), "Unexpected TEvUndelivered, reqId=%" PRIu64, ev->Cookie); + auto waitingInfo = waitingIt->second; + WaitingRequests.erase(waitingIt); + + switch (waitingInfo.Family) { + case AF_INET6: + ProcessErrorV6(waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver"); + break; + case AF_INET: + ProcessErrorV4(waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver"); + break; + default: + Y_FAIL("Unexpected request family %d", waitingInfo.Family); + } + + break; + } + + default: + Y_FAIL("Unexpected TEvUndelievered, type=%" PRIu32, ev->Get()->SourceType); + } + } + + private: + enum EIncomingRequestType { + GetHostByName, + GetAddr, + }; + + struct TIncomingRequest : public TIntrusiveListItem<TIncomingRequest> { + EIncomingRequestType Type; + TActorId Sender; + ui64 Cookie; + TString Name; + int Family; + }; + + using TIncomingRequestList = TIntrusiveListWithAutoDelete<TIncomingRequest, TDelete>; + + void EnqueueRequest(THolder<TIncomingRequest> req) { + if (MonCounters) { + ++*MonCounters->IncomingTotal; + } + + CleanupExpired(TActivationContext::Now()); + + switch (req->Family) { + case AF_UNSPEC: + if (Options.AllowIPv6) { + EnqueueRequestIPv6(std::move(req)); + return; + } + if (Options.AllowIPv4) { + EnqueueRequestIPv4(std::move(req)); + return; + } + break; + + case AF_INET6: + if (Options.AllowIPv6) { + EnqueueRequestIPv6(std::move(req)); + return; + } + break; + + case AF_INET: + if (Options.AllowIPv4) { + EnqueueRequestIPv4(std::move(req)); + return; + } + break; + } + + ReplyWithError(std::move(req), ARES_EBADFAMILY); + } + + void EnqueueRequestIPv6(THolder<TIncomingRequest> req) { + auto now = TActivationContext::Now(); + + auto& fullState = NameToState[req->Name]; + if (MonCounters) { + *MonCounters->CacheSize = NameToState.size(); + } + + auto& state = fullState.StateIPv6; + EnsureRequest(state, req->Name, AF_INET6, now); + + if (state.IsHardExpired(now)) { + Y_VERIFY(state.Waiting); + if (MonCounters) { + ++*MonCounters->CacheMisses; + } + // We need to wait for ipv6 reply, schedule ipv4 request in parallel if needed + if (Options.AllowIPv4) { + EnsureRequest(fullState.StateIPv4, req->Name, AF_INET, now); + } + state.WaitingRequests.PushBack(req.Release()); + return; + } + + // We want to retry AF_UNSPEC with IPv4 in some cases + if (req->Family == AF_UNSPEC && Options.AllowIPv4 && state.RetryUnspec()) { + EnqueueRequestIPv4(std::move(req)); + return; + } + + if (MonCounters) { + ++*MonCounters->CacheHits; + } + + if (state.Status != 0) { + ReplyWithError(std::move(req), state.Status, state.ErrorText); + } else { + ReplyWithAddrs(std::move(req), fullState.AddrsIPv6); + } + } + + void EnqueueRequestIPv4(THolder<TIncomingRequest> req, bool isCacheMiss = false) { + auto now = TActivationContext::Now(); + + auto& fullState = NameToState[req->Name]; + if (MonCounters) { + *MonCounters->CacheSize = NameToState.size(); + } + + auto& state = fullState.StateIPv4; + EnsureRequest(state, req->Name, AF_INET, now); + + if (state.IsHardExpired(now)) { + Y_VERIFY(state.Waiting); + if (MonCounters && !isCacheMiss) { + ++*MonCounters->CacheMisses; + } + state.WaitingRequests.PushBack(req.Release()); + return; + } + + if (MonCounters && !isCacheMiss) { + ++*MonCounters->CacheHits; + } + + if (state.Status != 0) { + ReplyWithError(std::move(req), state.Status, state.ErrorText); + } else { + ReplyWithAddrs(std::move(req), fullState.AddrsIPv4); + } + } + + private: + struct TFamilyState { + TIncomingRequestList WaitingRequests; + TInstant SoftDeadline; + TInstant HardDeadline; + TInstant NextSoftDeadline; + TInstant NextHardDeadline; + TString ErrorText; + int Status = -1; // never requested before + bool InSoftHeap = false; + bool InHardHeap = false; + bool Waiting = false; + + bool Needed() const { + return InSoftHeap || InHardHeap || Waiting; + } + + bool RetryUnspec() const { + return ( + Status == ARES_ENODATA || + Status == ARES_EBADRESP || + Status == ARES_ETIMEOUT); + } + + bool ServerReplied() const { + return ServerReplied(Status); + } + + bool IsSoftExpired(TInstant now) const { + return !InSoftHeap || NextSoftDeadline < now; + } + + bool IsHardExpired(TInstant now) const { + return !InHardHeap || NextHardDeadline < now; + } + + static bool ServerReplied(int status) { + return ( + status == ARES_SUCCESS || + status == ARES_ENODATA || + status == ARES_ENOTFOUND); + } + }; + + struct TState { + TFamilyState StateIPv6; + TFamilyState StateIPv4; + TVector<struct in6_addr> AddrsIPv6; + TVector<struct in_addr> AddrsIPv4; + + bool Needed() const { + return StateIPv6.Needed() || StateIPv4.Needed(); + } + }; + + using TNameToState = THashMap<TString, TState>; + + template<const TFamilyState TState::* StateToFamily, + const TInstant TFamilyState::* FamilyToDeadline> + struct THeapCompare { + // returns true when b < a + bool operator()(TNameToState::iterator a, TNameToState::iterator b) const { + const TState& aState = a->second; + const TState& bState = b->second; + const TFamilyState& aFamily = aState.*StateToFamily; + const TFamilyState& bFamily = bState.*StateToFamily; + const TInstant& aDeadline = aFamily.*FamilyToDeadline; + const TInstant& bDeadline = bFamily.*FamilyToDeadline; + return bDeadline < aDeadline; + } + }; + + template<const TFamilyState TState::* StateToFamily, + const TInstant TFamilyState::* FamilyToDeadline> + using TStateHeap = std::priority_queue< + TNameToState::iterator, + std::vector<TNameToState::iterator>, + THeapCompare<StateToFamily, FamilyToDeadline> + >; + + struct TWaitingInfo { + TNameToState::iterator Position; + int Family; + }; + + private: + void EnsureRequest(TFamilyState& state, const TString& name, int family, TInstant now) { + if (state.Waiting) { + return; // request is already pending + } + + if (!state.IsSoftExpired(now) && !state.IsHardExpired(now)) { + return; // response is not expired yet + } + + if (MonCounters) { + switch (family) { + case AF_INET6: + ++*MonCounters->OutgoingInFlightV6; + ++*MonCounters->OutgoingTotalV6; + break; + case AF_INET: + ++*MonCounters->OutgoingInFlightV4; + ++*MonCounters->OutgoingTotalV4; + break; + } + } + + ui64 reqId = ++LastRequestId; + auto& req = WaitingRequests[reqId]; + req.Position = NameToState.find(name); + req.Family = family; + Y_VERIFY(req.Position != NameToState.end()); + + Send(Upstream, new TEvDns::TEvGetHostByName(name, family), IEventHandle::FlagTrackDelivery, reqId); + state.Waiting = true; + } + + template<TFamilyState TState::* StateToFamily, + TInstant TFamilyState::* FamilyToDeadline, + TInstant TFamilyState::* FamilyToNextDeadline, + bool TFamilyState::* FamilyToFlag, + class THeap> + void PushToHeap(THeap& heap, TNameToState::iterator it, TInstant newDeadline) { + auto& family = it->second.*StateToFamily; + TInstant& deadline = family.*FamilyToDeadline; + TInstant& nextDeadline = family.*FamilyToNextDeadline; + bool& flag = family.*FamilyToFlag; + nextDeadline = newDeadline; + if (!flag) { + deadline = newDeadline; + heap.push(it); + flag = true; + } + } + + void PushSoftV6(TNameToState::iterator it, TInstant newDeadline) { + PushToHeap<&TState::StateIPv6, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv6, it, newDeadline); + } + + void PushHardV6(TNameToState::iterator it, TInstant newDeadline) { + PushToHeap<&TState::StateIPv6, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv6, it, newDeadline); + } + + void PushSoftV4(TNameToState::iterator it, TInstant newDeadline) { + PushToHeap<&TState::StateIPv4, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv4, it, newDeadline); + } + + void PushHardV4(TNameToState::iterator it, TInstant newDeadline) { + PushToHeap<&TState::StateIPv4, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv4, it, newDeadline); + } + + void ProcessErrorV6(TNameToState::iterator it, int status, TString errorText) { + auto now = TActivationContext::Now(); + if (MonCounters) { + --*MonCounters->OutgoingInFlightV6; + ++*MonCounters->OutgoingErrorsV6; + } + + auto& state = it->second.StateIPv6; + Y_VERIFY(state.Waiting, "Got error for a state we are not waiting"); + state.Waiting = false; + + // When we have a cached positive reply, don't overwrite it with spurious errors + const bool serverReplied = TFamilyState::ServerReplied(status); + if (!serverReplied && state.ServerReplied() && !state.IsHardExpired(now)) { + PushSoftV6(it, now + Options.SoftNegativeExpireTime); + if (state.Status == ARES_SUCCESS) { + SendAddrsV6(it); + } else { + SendErrorsV6(it, now); + } + return; + } + + state.Status = status; + state.ErrorText = std::move(errorText); + PushSoftV6(it, now + Options.SoftNegativeExpireTime); + if (serverReplied) { + // Server actually replied, so keep it cached for longer + PushHardV6(it, now + Options.HardPositiveExpireTime); + } else { + PushHardV6(it, now + Options.HardNegativeExpireTime); + } + + SendErrorsV6(it, now); + } + + void SendErrorsV6(TNameToState::iterator it, TInstant now) { + bool cleaned = false; + auto& state = it->second.StateIPv6; + while (state.WaitingRequests) { + THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); + if (req->Family == AF_UNSPEC && Options.AllowIPv4 && state.RetryUnspec()) { + if (!cleaned) { + CleanupExpired(now); + cleaned = true; + } + EnqueueRequestIPv4(std::move(req), /* isCacheMiss */ true); + } else { + ReplyWithError(std::move(req), state.Status, state.ErrorText); + } + } + } + + void ProcessErrorV4(TNameToState::iterator it, int status, TString errorText) { + auto now = TActivationContext::Now(); + if (MonCounters) { + --*MonCounters->OutgoingInFlightV4; + ++*MonCounters->OutgoingErrorsV4; + } + + auto& state = it->second.StateIPv4; + Y_VERIFY(state.Waiting, "Got error for a state we are not waiting"); + state.Waiting = false; + + // When we have a cached positive reply, don't overwrite it with spurious errors + const bool serverReplied = TFamilyState::ServerReplied(status); + if (!serverReplied && state.ServerReplied() && !state.IsHardExpired(now)) { + PushSoftV4(it, now + Options.SoftNegativeExpireTime); + if (state.Status == ARES_SUCCESS) { + SendAddrsV4(it); + } else { + SendErrorsV4(it); + } + return; + } + + state.Status = status; + state.ErrorText = std::move(errorText); + PushSoftV4(it, now + Options.SoftNegativeExpireTime); + if (serverReplied) { + // Server actually replied, so keep it cached for longer + PushHardV4(it, now + Options.HardPositiveExpireTime); + } else { + PushHardV4(it, now + Options.HardNegativeExpireTime); + } + + SendErrorsV4(it); + } + + void SendErrorsV4(TNameToState::iterator it) { + auto& state = it->second.StateIPv4; + while (state.WaitingRequests) { + THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); + ReplyWithError(std::move(req), state.Status, state.ErrorText); + } + } + + void ProcessAddrsV6(TNameToState::iterator it, TVector<struct in6_addr> addrs) { + if (Y_UNLIKELY(addrs.empty())) { + // Probably unnecessary: we don't want to deal with empty address lists + return ProcessErrorV6(it, ARES_ENODATA, ares_strerror(ARES_ENODATA)); + } + + auto now = TActivationContext::Now(); + if (MonCounters) { + --*MonCounters->OutgoingInFlightV6; + } + + auto& state = it->second.StateIPv6; + Y_VERIFY(state.Waiting, "Got reply for a state we are not waiting"); + state.Waiting = false; + + state.Status = ARES_SUCCESS; + it->second.AddrsIPv6 = std::move(addrs); + PushSoftV6(it, now + Options.SoftPositiveExpireTime); + PushHardV6(it, now + Options.HardPositiveExpireTime); + + SendAddrsV6(it); + } + + void SendAddrsV6(TNameToState::iterator it) { + auto& state = it->second.StateIPv6; + while (state.WaitingRequests) { + THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); + ReplyWithAddrs(std::move(req), it->second.AddrsIPv6); + } + } + + void ProcessAddrsV4(TNameToState::iterator it, TVector<struct in_addr> addrs) { + if (Y_UNLIKELY(addrs.empty())) { + // Probably unnecessary: we don't want to deal with empty address lists + return ProcessErrorV4(it, ARES_ENODATA, ares_strerror(ARES_ENODATA)); + } + + auto now = TActivationContext::Now(); + if (MonCounters) { + --*MonCounters->OutgoingInFlightV4; + } + + auto& state = it->second.StateIPv4; + Y_VERIFY(state.Waiting, "Got reply for a state we are not waiting"); + state.Waiting = false; + + state.Status = ARES_SUCCESS; + it->second.AddrsIPv4 = std::move(addrs); + PushSoftV4(it, now + Options.SoftPositiveExpireTime); + PushHardV4(it, now + Options.HardPositiveExpireTime); + + SendAddrsV4(it); + } + + void SendAddrsV4(TNameToState::iterator it) { + auto& state = it->second.StateIPv4; + while (state.WaitingRequests) { + THolder<TIncomingRequest> req(state.WaitingRequests.PopFront()); + ReplyWithAddrs(std::move(req), it->second.AddrsIPv4); + } + } + + private: + template<TFamilyState TState::*StateToFamily, + TInstant TFamilyState::* FamilyToDeadline, + TInstant TFamilyState::* FamilyToNextDeadline, + bool TFamilyState::* FamilyToFlag> + void DoCleanupExpired(TStateHeap<StateToFamily, FamilyToDeadline>& heap, TInstant now) { + while (!heap.empty()) { + auto it = heap.top(); + auto& family = it->second.*StateToFamily; + TInstant& deadline = family.*FamilyToDeadline; + if (now <= deadline) { + break; + } + + bool& flag = family.*FamilyToFlag; + Y_VERIFY(flag); + heap.pop(); + flag = false; + + TInstant& nextDeadline = family.*FamilyToNextDeadline; + if (now < nextDeadline) { + deadline = nextDeadline; + heap.push(it); + flag = true; + continue; + } + + // Remove unnecessary items + if (!it->second.Needed()) { + NameToState.erase(it); + if (MonCounters) { + *MonCounters->CacheSize = NameToState.size(); + } + } + } + } + + void CleanupExpired(TInstant now) { + DoCleanupExpired<&TState::StateIPv6, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv6, now); + DoCleanupExpired<&TState::StateIPv6, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv6, now); + DoCleanupExpired<&TState::StateIPv4, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv4, now); + DoCleanupExpired<&TState::StateIPv4, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv4, now); + } + + template<class TEvent> + void SendError(TActorId replyTo, ui64 cookie, int status, const TString& errorText) { + auto reply = MakeHolder<TEvent>(); + reply->Status = status; + reply->ErrorText = errorText; + this->Send(replyTo, reply.Release(), 0, cookie); + } + + void ReplyWithError(THolder<TIncomingRequest> req, int status, const TString& errorText) { + if (MonCounters) { + ++*MonCounters->IncomingErrors; + } + switch (req->Type) { + case EIncomingRequestType::GetHostByName: { + SendError<TEvDns::TEvGetHostByNameResult>(req->Sender, req->Cookie, status, errorText); + break; + } + case EIncomingRequestType::GetAddr: { + SendError<TEvDns::TEvGetAddrResult>(req->Sender, req->Cookie, status, errorText); + break; + } + } + } + + void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in6_addr>& addrs) { + switch (req->Type) { + case EIncomingRequestType::GetHostByName: { + auto reply = MakeHolder<TEvDns::TEvGetHostByNameResult>(); + reply->AddrsV6 = addrs; + Send(req->Sender, reply.Release(), 0, req->Cookie); + break; + } + case EIncomingRequestType::GetAddr: { + Y_VERIFY(!addrs.empty()); + auto reply = MakeHolder<TEvDns::TEvGetAddrResult>(); + reply->Addr = addrs.front(); + Send(req->Sender, reply.Release(), 0, req->Cookie); + break; + } + } + } + + void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in_addr>& addrs) { + switch (req->Type) { + case EIncomingRequestType::GetHostByName: { + auto reply = MakeHolder<TEvDns::TEvGetHostByNameResult>(); + reply->AddrsV4 = addrs; + Send(req->Sender, reply.Release(), 0, req->Cookie); + break; + } + case EIncomingRequestType::GetAddr: { + Y_VERIFY(!addrs.empty()); + auto reply = MakeHolder<TEvDns::TEvGetAddrResult>(); + reply->Addr = addrs.front(); + Send(req->Sender, reply.Release(), 0, req->Cookie); + break; + } + } + } + + void ReplyWithError(THolder<TIncomingRequest> req, int status) { + ReplyWithError(std::move(req), status, ares_strerror(status)); + } + + void DropPending(TIncomingRequestList& list, int status, const TString& errorText) { + while (list) { + THolder<TIncomingRequest> req(list.PopFront()); + ReplyWithError(std::move(req), status, errorText); + } + } + + void DropPending(int status, const TString& errorText) { + for (auto& [name, state] : NameToState) { + DropPending(state.StateIPv6.WaitingRequests, status, errorText); + DropPending(state.StateIPv4.WaitingRequests, status, errorText); + } + } + + void DropPending(int status) { + DropPending(status, ares_strerror(status)); + } + + private: + const TActorId Upstream; + const TCachingDnsResolverOptions Options; + const THolder<TMonCounters> MonCounters; + + TNameToState NameToState; + TStateHeap<&TState::StateIPv6, &TFamilyState::SoftDeadline> SoftHeapIPv6; + TStateHeap<&TState::StateIPv6, &TFamilyState::HardDeadline> HardHeapIPv6; + TStateHeap<&TState::StateIPv4, &TFamilyState::SoftDeadline> SoftHeapIPv4; + TStateHeap<&TState::StateIPv4, &TFamilyState::HardDeadline> HardHeapIPv4; + + THashMap<ui64, TWaitingInfo> WaitingRequests; + ui64 LastRequestId = 0; + }; + + IActor* CreateCachingDnsResolver(TActorId upstream, TCachingDnsResolverOptions options) { + return new TCachingDnsResolver(upstream, std::move(options)); + } + +} // namespace NDnsResolver +} // namespace NActors diff --git a/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp b/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp index c3b7cb3c77..f63e594d30 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp +++ b/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp @@ -1,630 +1,630 @@ -#include "dnsresolver.h" - -#include <library/cpp/actors/core/hfunc.h> -#include <library/cpp/actors/testlib/test_runtime.h> -#include <library/cpp/testing/unittest/registar.h> -#include <util/string/builder.h> - -#include <ares.h> - -using namespace NActors; -using namespace NActors::NDnsResolver; - -// FIXME: use a mock resolver -Y_UNIT_TEST_SUITE(CachingDnsResolver) { - - struct TAddrToString { - TString operator()(const std::monostate&) const { - return "<nothing>"; - } - - TString operator()(const struct in6_addr& addr) const { - char dst[INET6_ADDRSTRLEN]; - auto res = ares_inet_ntop(AF_INET6, &addr, dst, INET6_ADDRSTRLEN); - Y_VERIFY(res, "Cannot convert ipv6 address"); - return dst; - } - - TString operator()(const struct in_addr& addr) const { - char dst[INET_ADDRSTRLEN]; - auto res = ares_inet_ntop(AF_INET, &addr, dst, INET_ADDRSTRLEN); - Y_VERIFY(res, "Cannot convert ipv4 address"); - return dst; - } - }; - - TString AddrToString(const std::variant<std::monostate, struct in6_addr, struct in_addr>& v) { - return std::visit(TAddrToString(), v); - } - - struct TMockReply { - static constexpr TDuration DefaultDelay = TDuration::MilliSeconds(1); - - int Status = 0; - TDuration Delay; - TVector<struct in6_addr> AddrsV6; - TVector<struct in_addr> AddrsV4; - - static TMockReply Error(int status, TDuration delay = DefaultDelay) { - Y_VERIFY(status != 0); - TMockReply reply; - reply.Status = status; - reply.Delay = delay; - return reply; - } - - static TMockReply Empty(TDuration delay = DefaultDelay) { - TMockReply reply; - reply.Delay = delay; - return reply; - } - - static TMockReply ManyV6(const TVector<TString>& addrs, TDuration delay = DefaultDelay) { - TMockReply reply; - reply.Delay = delay; - for (const TString& addr : addrs) { - void* dst = &reply.AddrsV6.emplace_back(); - int status = ares_inet_pton(AF_INET6, addr.c_str(), dst); - Y_VERIFY(status == 1, "Invalid ipv6 address: %s", addr.c_str()); - } - return reply; - } - - static TMockReply ManyV4(const TVector<TString>& addrs, TDuration delay = DefaultDelay) { - TMockReply reply; - reply.Delay = delay; - for (const TString& addr : addrs) { - void* dst = &reply.AddrsV4.emplace_back(); - int status = ares_inet_pton(AF_INET, addr.c_str(), dst); - Y_VERIFY(status == 1, "Invalid ipv4 address: %s", addr.c_str()); - } - return reply; - } - - static TMockReply SingleV6(const TString& addr, TDuration delay = DefaultDelay) { - return ManyV6({ addr }, delay); - } - - static TMockReply SingleV4(const TString& addr, TDuration delay = DefaultDelay) { - return ManyV4({ addr }, delay); - } - }; - - using TMockDnsCallback = std::function<TMockReply (const TString&, int)>; - - class TMockDnsResolver : public TActor<TMockDnsResolver> { - public: - TMockDnsResolver(TMockDnsCallback callback) - : TActor(&TThis::StateWork) - , Callback(std::move(callback)) - { } - - private: - struct TEvPrivate { - enum EEv { - EvScheduled = EventSpaceBegin(TEvents::ES_PRIVATE), - }; - - struct TEvScheduled : public TEventLocal<TEvScheduled, EvScheduled> { - TActorId Sender; - ui64 Cookie; - TMockReply Reply; - - TEvScheduled(TActorId sender, ui64 cookie, TMockReply reply) - : Sender(sender) - , Cookie(cookie) - , Reply(std::move(reply)) - { } - }; - }; - - private: - STRICT_STFUNC(StateWork, { - hFunc(TEvents::TEvPoison, Handle); - hFunc(TEvDns::TEvGetHostByName, Handle); - hFunc(TEvPrivate::TEvScheduled, Handle); - }); - - void Handle(TEvents::TEvPoison::TPtr&) { - PassAway(); - } - - void Handle(TEvDns::TEvGetHostByName::TPtr& ev) { - auto reply = Callback(ev->Get()->Name, ev->Get()->Family); - if (reply.Delay) { - Schedule(reply.Delay, new TEvPrivate::TEvScheduled(ev->Sender, ev->Cookie, std::move(reply))); - } else { - SendReply(ev->Sender, ev->Cookie, std::move(reply)); - } - } - - void Handle(TEvPrivate::TEvScheduled::TPtr& ev) { - SendReply(ev->Get()->Sender, ev->Get()->Cookie, std::move(ev->Get()->Reply)); - } - - private: - void SendReply(const TActorId& sender, ui64 cookie, TMockReply&& reply) { - auto res = MakeHolder<TEvDns::TEvGetHostByNameResult>(); - res->Status = reply.Status; - if (res->Status != 0) { - res->ErrorText = ares_strerror(res->Status); - } else { - res->AddrsV6 = std::move(reply.AddrsV6); - res->AddrsV4 = std::move(reply.AddrsV4); - } - Send(sender, res.Release(), 0, cookie); - } - - private: - TMockDnsCallback Callback; - }; - - struct TCachingDnsRuntime : public TTestActorRuntimeBase { - TCachingDnsResolverOptions ResolverOptions; - TActorId MockResolver; - TActorId Resolver; - TActorId Sleeper; - TString Section_; - - NMonitoring::TDynamicCounters::TCounterPtr InFlight6; - NMonitoring::TDynamicCounters::TCounterPtr InFlight4; - NMonitoring::TDynamicCounters::TCounterPtr Total6; - NMonitoring::TDynamicCounters::TCounterPtr Total4; - NMonitoring::TDynamicCounters::TCounterPtr Misses; - NMonitoring::TDynamicCounters::TCounterPtr Hits; - - THashMap<TString, TMockReply> ReplyV6; - THashMap<TString, TMockReply> ReplyV4; - - TCachingDnsRuntime() { - SetScheduledEventFilter([](auto&&, auto&&, auto&&, auto&&) { return false; }); - ResolverOptions.MonCounters = new NMonitoring::TDynamicCounters; - - ReplyV6["localhost"] = TMockReply::SingleV6("::1"); - ReplyV4["localhost"] = TMockReply::SingleV4("127.0.0.1"); - ReplyV6["yandex.ru"] = TMockReply::SingleV6("2a02:6b8:a::a", TDuration::MilliSeconds(500)); - ReplyV4["yandex.ru"] = TMockReply::SingleV4("77.88.55.77", TDuration::MilliSeconds(250)); - ReplyV6["router.asus.com"] = TMockReply::Error(ARES_ENODATA); - ReplyV4["router.asus.com"] = TMockReply::SingleV4("192.168.0.1"); - } - - void Start(TMockDnsCallback callback) { - MockResolver = Register(new TMockDnsResolver(std::move(callback))); - EnableScheduleForActor(MockResolver); - Resolver = Register(CreateCachingDnsResolver(MockResolver, ResolverOptions)); - Sleeper = AllocateEdgeActor(); - - InFlight6 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/V6", false); - InFlight4 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/V4", false); - Total6 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/V6", true); - Total4 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/V4", true); - Misses = ResolverOptions.MonCounters->GetCounter("DnsResolver/Cache/Misses", true); - Hits = ResolverOptions.MonCounters->GetCounter("DnsResolver/Cache/Hits", true); - } - - void Start() { - Start([this](const TString& name, int family) { - switch (family) { - case AF_INET6: { - auto it = ReplyV6.find(name); - if (it != ReplyV6.end()) { - return it->second; - } - break; - } - case AF_INET: { - auto it = ReplyV4.find(name); - if (it != ReplyV4.end()) { - return it->second; - } - break; - } - } - return TMockReply::Error(ARES_ENOTFOUND); - }); - } - - void Section(const TString& section) { - Section_ = section; - } - - void Sleep(TDuration duration) { - Schedule(new IEventHandle(Sleeper, Sleeper, new TEvents::TEvWakeup), duration); - GrabEdgeEventRethrow<TEvents::TEvWakeup>(Sleeper); - } - - void WaitNoInFlight() { - if (*InFlight6 || *InFlight4) { - TDispatchOptions options; - options.CustomFinalCondition = [&]() { - return !*InFlight6 && !*InFlight4; - }; - DispatchEvents(options); - UNIT_ASSERT_C(!*InFlight6 && !*InFlight4, "Failed to wait for no inflight in " << Section_); - } - } - - void SendGetHostByName(const TActorId& sender, const TString& name, int family = AF_UNSPEC) { - Send(new IEventHandle(Resolver, sender, new TEvDns::TEvGetHostByName(name, family)), 0, true); - } - - void SendGetAddr(const TActorId& sender, const TString& name, int family = AF_UNSPEC) { - Send(new IEventHandle(Resolver, sender, new TEvDns::TEvGetAddr(name, family)), 0, true); - } - - TEvDns::TEvGetHostByNameResult::TPtr WaitGetHostByName(const TActorId& sender) { - return GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); - } - - TEvDns::TEvGetAddrResult::TPtr WaitGetAddr(const TActorId& sender) { - return GrabEdgeEventRethrow<TEvDns::TEvGetAddrResult>(sender); - } - - void ExpectInFlight6(i64 count) { - UNIT_ASSERT_VALUES_EQUAL_C(InFlight6->Val(), count, Section_); - } - - void ExpectInFlight4(i64 count) { - UNIT_ASSERT_VALUES_EQUAL_C(InFlight4->Val(), count, Section_); - } - - void ExpectTotal6(i64 count) { - UNIT_ASSERT_VALUES_EQUAL_C(Total6->Val(), count, Section_); - } - - void ExpectTotal4(i64 count) { - UNIT_ASSERT_VALUES_EQUAL_C(Total4->Val(), count, Section_); - } - - void Expect6(i64 total, i64 inflight) { - UNIT_ASSERT_C( - Total6->Val() == total && InFlight6->Val() == inflight, - Section_ << ": Expect6(" << total << ", " << inflight << ") " - << " but got (" << Total6->Val() << ", " << InFlight6->Val() << ")"); - } - - void Expect4(i64 total, i64 inflight) { - UNIT_ASSERT_C( - Total4->Val() == total && InFlight4->Val() == inflight, - Section_ << ": Expect4(" << total << ", " << inflight << ") " - << " got (" << Total4->Val() << ", " << InFlight4->Val() << ")"); - } - - void ExpectMisses(i64 count) { - UNIT_ASSERT_VALUES_EQUAL_C(Misses->Val(), count, Section_); - } - - void ExpectHits(i64 count) { - UNIT_ASSERT_VALUES_EQUAL_C(Hits->Val(), count, Section_); - } - - void ExpectGetHostByNameError(const TActorId& sender, int status) { - auto ev = WaitGetHostByName(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, status, Section_ << ": " << ev->Get()->ErrorText); - } - - void ExpectGetAddrError(const TActorId& sender, int status) { - auto ev = WaitGetAddr(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, status, Section_ << ": " << ev->Get()->ErrorText); - } - - void ExpectGetHostByNameSuccess(const TActorId& sender, const TString& expected) { - auto ev = WaitGetHostByName(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, Section_ << ": " << ev->Get()->ErrorText); - TStringBuilder result; - for (const auto& addr : ev->Get()->AddrsV6) { - if (result) { - result << ','; - } - result << TAddrToString()(addr); - } - for (const auto& addr : ev->Get()->AddrsV4) { - if (result) { - result << ','; - } - result << TAddrToString()(addr); - } - UNIT_ASSERT_VALUES_EQUAL_C(TString(result), expected, Section_); - } - - void ExpectGetAddrSuccess(const TActorId& sender, const TString& expected) { - auto ev = WaitGetAddr(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, Section_ << ": " << ev->Get()->ErrorText); - TString result = AddrToString(ev->Get()->Addr); - UNIT_ASSERT_VALUES_EQUAL_C(result, expected, Section_); - } - }; - - Y_UNIT_TEST(UnusableResolver) { - TCachingDnsRuntime runtime; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - - runtime.Send(new IEventHandle(runtime.MockResolver, { }, new TEvents::TEvPoison), 0, true); - runtime.SendGetAddr(sender, "foo.ru", AF_UNSPEC); - runtime.ExpectGetAddrError(sender, ARES_ENOTINITIALIZED); - } - - Y_UNIT_TEST(ResolveCaching) { - TCachingDnsRuntime runtime; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - // First time resolve, ipv4 and ipv6 sent in parallel, we wait for ipv6 result - runtime.Section("First time resolve"); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.Expect6(1, 0); - runtime.Expect4(1, 0); - runtime.ExpectMisses(1); - runtime.ExpectHits(0); - - // Second resolve, ipv6 and ipv4 queries result in a cache hit - runtime.Section("Second resolve, ipv6"); - runtime.SendGetAddr(sender, "yandex.ru", AF_INET6); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.Expect6(1, 0); - runtime.ExpectHits(1); - runtime.Section("Second resolve, ipv4"); - runtime.SendGetAddr(sender, "yandex.ru", AF_INET); - runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); - runtime.Expect4(1, 0); - runtime.ExpectHits(2); - - // Wait until soft expiration and try ipv4 again - // Will cause a cache hit, but will start a new ipv4 request in background - runtime.Section("Retry ipv4 after soft expiration"); - runtime.Sleep(TDuration::Seconds(15)); - runtime.SendGetAddr(sender, "yandex.ru", AF_INET); - runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); - runtime.Expect6(1, 0); - runtime.Expect4(2, 1); - runtime.ExpectMisses(1); - runtime.ExpectHits(3); - runtime.WaitNoInFlight(); - - // Wait until soft expiration and try both again - // Will cause a cache hit, but will start a new ipv6 request in background - runtime.Section("Retry both after soft expiration"); - runtime.Sleep(TDuration::Seconds(15)); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.Expect6(2, 1); - runtime.Expect4(2, 0); - runtime.ExpectMisses(1); - runtime.ExpectHits(4); - runtime.WaitNoInFlight(); - - // Wait until hard expiration and try both again - // Will cause a cache miss and new resolve requests - runtime.Section("Retry both after hard expiration"); - runtime.Sleep(TDuration::Hours(2)); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.Expect6(3, 0); - runtime.Expect4(3, 0); - runtime.ExpectMisses(2); - runtime.ExpectHits(4); - - // Wait half the hard expiration time, must always result in a cache hit - runtime.Section("Retry both after half hard expiration"); - for (ui64 i = 1; i <= 4; ++i) { - runtime.Sleep(TDuration::Hours(1)); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.Expect6(3 + i, 1); - runtime.ExpectHits(4 + i); - runtime.WaitNoInFlight(); - } - - // Change v6 result to a timeout, must keep using cached result until hard expiration - runtime.Section("Dns keeps timing out"); - runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ETIMEOUT); - for (ui64 i = 1; i <= 4; ++i) { - runtime.Sleep(TDuration::Seconds(15)); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.Expect6(7 + i, 1); - runtime.ExpectHits(8 + i); - runtime.WaitNoInFlight(); - } - - // Change v6 result to nodata, must switch to a v4 result eventually - runtime.Section("Host changes to being ipv4 only"); - runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ENODATA); - runtime.Sleep(TDuration::Seconds(2)); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - runtime.WaitNoInFlight(); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); - runtime.Expect6(12, 0); - runtime.Expect4(4, 0); - runtime.ExpectMisses(3); - - // Change v6 result to nxdomain, must not fall back to a v4 result - runtime.Section("Host is removed from dns"); - runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ENOTFOUND); - runtime.Sleep(TDuration::Seconds(15)); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); - runtime.WaitNoInFlight(); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - } - - Y_UNIT_TEST(ResolveCachingV4) { - TCachingDnsRuntime runtime; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.Section("First request"); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); - runtime.ExpectMisses(1); - - runtime.Section("Second request"); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); - runtime.ExpectHits(1); - - runtime.Section("Dns keeps timing out"); - runtime.ReplyV6["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT); - runtime.ReplyV4["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT); - for (ui64 i = 1; i <= 4; ++i) { - runtime.Sleep(TDuration::Seconds(15)); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); - runtime.Expect6(1 + i, 1); - runtime.Expect4(1 + i, 1); - runtime.ExpectHits(1 + i); - runtime.WaitNoInFlight(); - } - - runtime.Section("Host is removed from ipv4 dns"); - runtime.ReplyV4["router.asus.com"] = TMockReply::Error(ARES_ENOTFOUND); - runtime.Sleep(TDuration::Seconds(15)); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); - runtime.WaitNoInFlight(); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - } - - Y_UNIT_TEST(EventualTimeout) { - TCachingDnsRuntime runtime; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.ReplyV6["notfound.ru"] = TMockReply::Error(ARES_ENODATA); - runtime.ReplyV4["notfound.ru"] = TMockReply::Error(ARES_ENOTFOUND); - runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - - runtime.ReplyV4["notfound.ru"] = TMockReply::Error(ARES_ETIMEOUT); - runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - runtime.WaitNoInFlight(); - - bool timeout = false; - for (ui64 i = 1; i <= 8; ++i) { - runtime.Sleep(TDuration::Minutes(30)); - runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); - auto ev = runtime.WaitGetAddr(sender); - if (ev->Get()->Status == ARES_ETIMEOUT && i > 2) { - timeout = true; - break; - } - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, ARES_ENOTFOUND, - "Iteration " << i << ": " << ev->Get()->ErrorText); - } - - UNIT_ASSERT_C(timeout, "DnsResolver did not reply with a timeout"); - } - - Y_UNIT_TEST(MultipleRequestsAndHosts) { - TCachingDnsRuntime runtime; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.SendGetHostByName(sender, "router.asus.com", AF_UNSPEC); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetHostByNameSuccess(sender, "192.168.0.1"); - runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); - runtime.ExpectGetHostByNameSuccess(sender, "2a02:6b8:a::a"); - runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); - - runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); - runtime.ExpectGetHostByNameError(sender, ARES_ENOTFOUND); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - } - - Y_UNIT_TEST(DisabledIPv6) { - TCachingDnsRuntime runtime; - runtime.ResolverOptions.AllowIPv6 = false; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetHostByNameSuccess(sender, "77.88.55.77"); - runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); - - runtime.SendGetHostByName(sender, "yandex.ru", AF_INET6); - runtime.SendGetAddr(sender, "yandex.ru", AF_INET6); - runtime.ExpectGetHostByNameError(sender, ARES_EBADFAMILY); - runtime.ExpectGetAddrError(sender, ARES_EBADFAMILY); - - runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.ExpectGetHostByNameSuccess(sender, "77.88.55.77"); - runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); - - runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); - runtime.ExpectGetHostByNameError(sender, ARES_ENOTFOUND); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - } - - Y_UNIT_TEST(DisabledIPv4) { - TCachingDnsRuntime runtime; - runtime.ResolverOptions.AllowIPv4 = false; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.SendGetHostByName(sender, "router.asus.com", AF_UNSPEC); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetHostByNameError(sender, ARES_ENODATA); - runtime.ExpectGetAddrError(sender, ARES_ENODATA); - - runtime.SendGetHostByName(sender, "router.asus.com", AF_INET); - runtime.SendGetAddr(sender, "router.asus.com", AF_INET); - runtime.ExpectGetHostByNameError(sender, ARES_EBADFAMILY); - runtime.ExpectGetAddrError(sender, ARES_EBADFAMILY); - - runtime.SendGetHostByName(sender, "router.asus.com", AF_UNSPEC); - runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); - runtime.ExpectGetHostByNameError(sender, ARES_ENODATA); - runtime.ExpectGetAddrError(sender, ARES_ENODATA); - - runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); - runtime.ExpectGetHostByNameError(sender, ARES_ENOTFOUND); - runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); - } - - Y_UNIT_TEST(PoisonPill) { - TCachingDnsRuntime runtime; - runtime.Initialize(); - runtime.Start(); - - auto sender = runtime.AllocateEdgeActor(); - - runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); - runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); - runtime.Send(new IEventHandle(runtime.Resolver, sender, new TEvents::TEvPoison), 0, true); - runtime.ExpectGetHostByNameError(sender, ARES_ECANCELLED); - runtime.ExpectGetAddrError(sender, ARES_ECANCELLED); - } - -} +#include "dnsresolver.h" + +#include <library/cpp/actors/core/hfunc.h> +#include <library/cpp/actors/testlib/test_runtime.h> +#include <library/cpp/testing/unittest/registar.h> +#include <util/string/builder.h> + +#include <ares.h> + +using namespace NActors; +using namespace NActors::NDnsResolver; + +// FIXME: use a mock resolver +Y_UNIT_TEST_SUITE(CachingDnsResolver) { + + struct TAddrToString { + TString operator()(const std::monostate&) const { + return "<nothing>"; + } + + TString operator()(const struct in6_addr& addr) const { + char dst[INET6_ADDRSTRLEN]; + auto res = ares_inet_ntop(AF_INET6, &addr, dst, INET6_ADDRSTRLEN); + Y_VERIFY(res, "Cannot convert ipv6 address"); + return dst; + } + + TString operator()(const struct in_addr& addr) const { + char dst[INET_ADDRSTRLEN]; + auto res = ares_inet_ntop(AF_INET, &addr, dst, INET_ADDRSTRLEN); + Y_VERIFY(res, "Cannot convert ipv4 address"); + return dst; + } + }; + + TString AddrToString(const std::variant<std::monostate, struct in6_addr, struct in_addr>& v) { + return std::visit(TAddrToString(), v); + } + + struct TMockReply { + static constexpr TDuration DefaultDelay = TDuration::MilliSeconds(1); + + int Status = 0; + TDuration Delay; + TVector<struct in6_addr> AddrsV6; + TVector<struct in_addr> AddrsV4; + + static TMockReply Error(int status, TDuration delay = DefaultDelay) { + Y_VERIFY(status != 0); + TMockReply reply; + reply.Status = status; + reply.Delay = delay; + return reply; + } + + static TMockReply Empty(TDuration delay = DefaultDelay) { + TMockReply reply; + reply.Delay = delay; + return reply; + } + + static TMockReply ManyV6(const TVector<TString>& addrs, TDuration delay = DefaultDelay) { + TMockReply reply; + reply.Delay = delay; + for (const TString& addr : addrs) { + void* dst = &reply.AddrsV6.emplace_back(); + int status = ares_inet_pton(AF_INET6, addr.c_str(), dst); + Y_VERIFY(status == 1, "Invalid ipv6 address: %s", addr.c_str()); + } + return reply; + } + + static TMockReply ManyV4(const TVector<TString>& addrs, TDuration delay = DefaultDelay) { + TMockReply reply; + reply.Delay = delay; + for (const TString& addr : addrs) { + void* dst = &reply.AddrsV4.emplace_back(); + int status = ares_inet_pton(AF_INET, addr.c_str(), dst); + Y_VERIFY(status == 1, "Invalid ipv4 address: %s", addr.c_str()); + } + return reply; + } + + static TMockReply SingleV6(const TString& addr, TDuration delay = DefaultDelay) { + return ManyV6({ addr }, delay); + } + + static TMockReply SingleV4(const TString& addr, TDuration delay = DefaultDelay) { + return ManyV4({ addr }, delay); + } + }; + + using TMockDnsCallback = std::function<TMockReply (const TString&, int)>; + + class TMockDnsResolver : public TActor<TMockDnsResolver> { + public: + TMockDnsResolver(TMockDnsCallback callback) + : TActor(&TThis::StateWork) + , Callback(std::move(callback)) + { } + + private: + struct TEvPrivate { + enum EEv { + EvScheduled = EventSpaceBegin(TEvents::ES_PRIVATE), + }; + + struct TEvScheduled : public TEventLocal<TEvScheduled, EvScheduled> { + TActorId Sender; + ui64 Cookie; + TMockReply Reply; + + TEvScheduled(TActorId sender, ui64 cookie, TMockReply reply) + : Sender(sender) + , Cookie(cookie) + , Reply(std::move(reply)) + { } + }; + }; + + private: + STRICT_STFUNC(StateWork, { + hFunc(TEvents::TEvPoison, Handle); + hFunc(TEvDns::TEvGetHostByName, Handle); + hFunc(TEvPrivate::TEvScheduled, Handle); + }); + + void Handle(TEvents::TEvPoison::TPtr&) { + PassAway(); + } + + void Handle(TEvDns::TEvGetHostByName::TPtr& ev) { + auto reply = Callback(ev->Get()->Name, ev->Get()->Family); + if (reply.Delay) { + Schedule(reply.Delay, new TEvPrivate::TEvScheduled(ev->Sender, ev->Cookie, std::move(reply))); + } else { + SendReply(ev->Sender, ev->Cookie, std::move(reply)); + } + } + + void Handle(TEvPrivate::TEvScheduled::TPtr& ev) { + SendReply(ev->Get()->Sender, ev->Get()->Cookie, std::move(ev->Get()->Reply)); + } + + private: + void SendReply(const TActorId& sender, ui64 cookie, TMockReply&& reply) { + auto res = MakeHolder<TEvDns::TEvGetHostByNameResult>(); + res->Status = reply.Status; + if (res->Status != 0) { + res->ErrorText = ares_strerror(res->Status); + } else { + res->AddrsV6 = std::move(reply.AddrsV6); + res->AddrsV4 = std::move(reply.AddrsV4); + } + Send(sender, res.Release(), 0, cookie); + } + + private: + TMockDnsCallback Callback; + }; + + struct TCachingDnsRuntime : public TTestActorRuntimeBase { + TCachingDnsResolverOptions ResolverOptions; + TActorId MockResolver; + TActorId Resolver; + TActorId Sleeper; + TString Section_; + + NMonitoring::TDynamicCounters::TCounterPtr InFlight6; + NMonitoring::TDynamicCounters::TCounterPtr InFlight4; + NMonitoring::TDynamicCounters::TCounterPtr Total6; + NMonitoring::TDynamicCounters::TCounterPtr Total4; + NMonitoring::TDynamicCounters::TCounterPtr Misses; + NMonitoring::TDynamicCounters::TCounterPtr Hits; + + THashMap<TString, TMockReply> ReplyV6; + THashMap<TString, TMockReply> ReplyV4; + + TCachingDnsRuntime() { + SetScheduledEventFilter([](auto&&, auto&&, auto&&, auto&&) { return false; }); + ResolverOptions.MonCounters = new NMonitoring::TDynamicCounters; + + ReplyV6["localhost"] = TMockReply::SingleV6("::1"); + ReplyV4["localhost"] = TMockReply::SingleV4("127.0.0.1"); + ReplyV6["yandex.ru"] = TMockReply::SingleV6("2a02:6b8:a::a", TDuration::MilliSeconds(500)); + ReplyV4["yandex.ru"] = TMockReply::SingleV4("77.88.55.77", TDuration::MilliSeconds(250)); + ReplyV6["router.asus.com"] = TMockReply::Error(ARES_ENODATA); + ReplyV4["router.asus.com"] = TMockReply::SingleV4("192.168.0.1"); + } + + void Start(TMockDnsCallback callback) { + MockResolver = Register(new TMockDnsResolver(std::move(callback))); + EnableScheduleForActor(MockResolver); + Resolver = Register(CreateCachingDnsResolver(MockResolver, ResolverOptions)); + Sleeper = AllocateEdgeActor(); + + InFlight6 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/V6", false); + InFlight4 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/V4", false); + Total6 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/V6", true); + Total4 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/V4", true); + Misses = ResolverOptions.MonCounters->GetCounter("DnsResolver/Cache/Misses", true); + Hits = ResolverOptions.MonCounters->GetCounter("DnsResolver/Cache/Hits", true); + } + + void Start() { + Start([this](const TString& name, int family) { + switch (family) { + case AF_INET6: { + auto it = ReplyV6.find(name); + if (it != ReplyV6.end()) { + return it->second; + } + break; + } + case AF_INET: { + auto it = ReplyV4.find(name); + if (it != ReplyV4.end()) { + return it->second; + } + break; + } + } + return TMockReply::Error(ARES_ENOTFOUND); + }); + } + + void Section(const TString& section) { + Section_ = section; + } + + void Sleep(TDuration duration) { + Schedule(new IEventHandle(Sleeper, Sleeper, new TEvents::TEvWakeup), duration); + GrabEdgeEventRethrow<TEvents::TEvWakeup>(Sleeper); + } + + void WaitNoInFlight() { + if (*InFlight6 || *InFlight4) { + TDispatchOptions options; + options.CustomFinalCondition = [&]() { + return !*InFlight6 && !*InFlight4; + }; + DispatchEvents(options); + UNIT_ASSERT_C(!*InFlight6 && !*InFlight4, "Failed to wait for no inflight in " << Section_); + } + } + + void SendGetHostByName(const TActorId& sender, const TString& name, int family = AF_UNSPEC) { + Send(new IEventHandle(Resolver, sender, new TEvDns::TEvGetHostByName(name, family)), 0, true); + } + + void SendGetAddr(const TActorId& sender, const TString& name, int family = AF_UNSPEC) { + Send(new IEventHandle(Resolver, sender, new TEvDns::TEvGetAddr(name, family)), 0, true); + } + + TEvDns::TEvGetHostByNameResult::TPtr WaitGetHostByName(const TActorId& sender) { + return GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); + } + + TEvDns::TEvGetAddrResult::TPtr WaitGetAddr(const TActorId& sender) { + return GrabEdgeEventRethrow<TEvDns::TEvGetAddrResult>(sender); + } + + void ExpectInFlight6(i64 count) { + UNIT_ASSERT_VALUES_EQUAL_C(InFlight6->Val(), count, Section_); + } + + void ExpectInFlight4(i64 count) { + UNIT_ASSERT_VALUES_EQUAL_C(InFlight4->Val(), count, Section_); + } + + void ExpectTotal6(i64 count) { + UNIT_ASSERT_VALUES_EQUAL_C(Total6->Val(), count, Section_); + } + + void ExpectTotal4(i64 count) { + UNIT_ASSERT_VALUES_EQUAL_C(Total4->Val(), count, Section_); + } + + void Expect6(i64 total, i64 inflight) { + UNIT_ASSERT_C( + Total6->Val() == total && InFlight6->Val() == inflight, + Section_ << ": Expect6(" << total << ", " << inflight << ") " + << " but got (" << Total6->Val() << ", " << InFlight6->Val() << ")"); + } + + void Expect4(i64 total, i64 inflight) { + UNIT_ASSERT_C( + Total4->Val() == total && InFlight4->Val() == inflight, + Section_ << ": Expect4(" << total << ", " << inflight << ") " + << " got (" << Total4->Val() << ", " << InFlight4->Val() << ")"); + } + + void ExpectMisses(i64 count) { + UNIT_ASSERT_VALUES_EQUAL_C(Misses->Val(), count, Section_); + } + + void ExpectHits(i64 count) { + UNIT_ASSERT_VALUES_EQUAL_C(Hits->Val(), count, Section_); + } + + void ExpectGetHostByNameError(const TActorId& sender, int status) { + auto ev = WaitGetHostByName(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, status, Section_ << ": " << ev->Get()->ErrorText); + } + + void ExpectGetAddrError(const TActorId& sender, int status) { + auto ev = WaitGetAddr(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, status, Section_ << ": " << ev->Get()->ErrorText); + } + + void ExpectGetHostByNameSuccess(const TActorId& sender, const TString& expected) { + auto ev = WaitGetHostByName(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, Section_ << ": " << ev->Get()->ErrorText); + TStringBuilder result; + for (const auto& addr : ev->Get()->AddrsV6) { + if (result) { + result << ','; + } + result << TAddrToString()(addr); + } + for (const auto& addr : ev->Get()->AddrsV4) { + if (result) { + result << ','; + } + result << TAddrToString()(addr); + } + UNIT_ASSERT_VALUES_EQUAL_C(TString(result), expected, Section_); + } + + void ExpectGetAddrSuccess(const TActorId& sender, const TString& expected) { + auto ev = WaitGetAddr(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, Section_ << ": " << ev->Get()->ErrorText); + TString result = AddrToString(ev->Get()->Addr); + UNIT_ASSERT_VALUES_EQUAL_C(result, expected, Section_); + } + }; + + Y_UNIT_TEST(UnusableResolver) { + TCachingDnsRuntime runtime; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + + runtime.Send(new IEventHandle(runtime.MockResolver, { }, new TEvents::TEvPoison), 0, true); + runtime.SendGetAddr(sender, "foo.ru", AF_UNSPEC); + runtime.ExpectGetAddrError(sender, ARES_ENOTINITIALIZED); + } + + Y_UNIT_TEST(ResolveCaching) { + TCachingDnsRuntime runtime; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + // First time resolve, ipv4 and ipv6 sent in parallel, we wait for ipv6 result + runtime.Section("First time resolve"); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.Expect6(1, 0); + runtime.Expect4(1, 0); + runtime.ExpectMisses(1); + runtime.ExpectHits(0); + + // Second resolve, ipv6 and ipv4 queries result in a cache hit + runtime.Section("Second resolve, ipv6"); + runtime.SendGetAddr(sender, "yandex.ru", AF_INET6); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.Expect6(1, 0); + runtime.ExpectHits(1); + runtime.Section("Second resolve, ipv4"); + runtime.SendGetAddr(sender, "yandex.ru", AF_INET); + runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); + runtime.Expect4(1, 0); + runtime.ExpectHits(2); + + // Wait until soft expiration and try ipv4 again + // Will cause a cache hit, but will start a new ipv4 request in background + runtime.Section("Retry ipv4 after soft expiration"); + runtime.Sleep(TDuration::Seconds(15)); + runtime.SendGetAddr(sender, "yandex.ru", AF_INET); + runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); + runtime.Expect6(1, 0); + runtime.Expect4(2, 1); + runtime.ExpectMisses(1); + runtime.ExpectHits(3); + runtime.WaitNoInFlight(); + + // Wait until soft expiration and try both again + // Will cause a cache hit, but will start a new ipv6 request in background + runtime.Section("Retry both after soft expiration"); + runtime.Sleep(TDuration::Seconds(15)); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.Expect6(2, 1); + runtime.Expect4(2, 0); + runtime.ExpectMisses(1); + runtime.ExpectHits(4); + runtime.WaitNoInFlight(); + + // Wait until hard expiration and try both again + // Will cause a cache miss and new resolve requests + runtime.Section("Retry both after hard expiration"); + runtime.Sleep(TDuration::Hours(2)); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.Expect6(3, 0); + runtime.Expect4(3, 0); + runtime.ExpectMisses(2); + runtime.ExpectHits(4); + + // Wait half the hard expiration time, must always result in a cache hit + runtime.Section("Retry both after half hard expiration"); + for (ui64 i = 1; i <= 4; ++i) { + runtime.Sleep(TDuration::Hours(1)); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.Expect6(3 + i, 1); + runtime.ExpectHits(4 + i); + runtime.WaitNoInFlight(); + } + + // Change v6 result to a timeout, must keep using cached result until hard expiration + runtime.Section("Dns keeps timing out"); + runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ETIMEOUT); + for (ui64 i = 1; i <= 4; ++i) { + runtime.Sleep(TDuration::Seconds(15)); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.Expect6(7 + i, 1); + runtime.ExpectHits(8 + i); + runtime.WaitNoInFlight(); + } + + // Change v6 result to nodata, must switch to a v4 result eventually + runtime.Section("Host changes to being ipv4 only"); + runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ENODATA); + runtime.Sleep(TDuration::Seconds(2)); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + runtime.WaitNoInFlight(); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); + runtime.Expect6(12, 0); + runtime.Expect4(4, 0); + runtime.ExpectMisses(3); + + // Change v6 result to nxdomain, must not fall back to a v4 result + runtime.Section("Host is removed from dns"); + runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ENOTFOUND); + runtime.Sleep(TDuration::Seconds(15)); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); + runtime.WaitNoInFlight(); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + } + + Y_UNIT_TEST(ResolveCachingV4) { + TCachingDnsRuntime runtime; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.Section("First request"); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); + runtime.ExpectMisses(1); + + runtime.Section("Second request"); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); + runtime.ExpectHits(1); + + runtime.Section("Dns keeps timing out"); + runtime.ReplyV6["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT); + runtime.ReplyV4["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT); + for (ui64 i = 1; i <= 4; ++i) { + runtime.Sleep(TDuration::Seconds(15)); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); + runtime.Expect6(1 + i, 1); + runtime.Expect4(1 + i, 1); + runtime.ExpectHits(1 + i); + runtime.WaitNoInFlight(); + } + + runtime.Section("Host is removed from ipv4 dns"); + runtime.ReplyV4["router.asus.com"] = TMockReply::Error(ARES_ENOTFOUND); + runtime.Sleep(TDuration::Seconds(15)); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); + runtime.WaitNoInFlight(); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + } + + Y_UNIT_TEST(EventualTimeout) { + TCachingDnsRuntime runtime; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.ReplyV6["notfound.ru"] = TMockReply::Error(ARES_ENODATA); + runtime.ReplyV4["notfound.ru"] = TMockReply::Error(ARES_ENOTFOUND); + runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + + runtime.ReplyV4["notfound.ru"] = TMockReply::Error(ARES_ETIMEOUT); + runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + runtime.WaitNoInFlight(); + + bool timeout = false; + for (ui64 i = 1; i <= 8; ++i) { + runtime.Sleep(TDuration::Minutes(30)); + runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); + auto ev = runtime.WaitGetAddr(sender); + if (ev->Get()->Status == ARES_ETIMEOUT && i > 2) { + timeout = true; + break; + } + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, ARES_ENOTFOUND, + "Iteration " << i << ": " << ev->Get()->ErrorText); + } + + UNIT_ASSERT_C(timeout, "DnsResolver did not reply with a timeout"); + } + + Y_UNIT_TEST(MultipleRequestsAndHosts) { + TCachingDnsRuntime runtime; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.SendGetHostByName(sender, "router.asus.com", AF_UNSPEC); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetHostByNameSuccess(sender, "192.168.0.1"); + runtime.ExpectGetAddrSuccess(sender, "192.168.0.1"); + runtime.ExpectGetHostByNameSuccess(sender, "2a02:6b8:a::a"); + runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a"); + + runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); + runtime.ExpectGetHostByNameError(sender, ARES_ENOTFOUND); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + } + + Y_UNIT_TEST(DisabledIPv6) { + TCachingDnsRuntime runtime; + runtime.ResolverOptions.AllowIPv6 = false; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetHostByNameSuccess(sender, "77.88.55.77"); + runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); + + runtime.SendGetHostByName(sender, "yandex.ru", AF_INET6); + runtime.SendGetAddr(sender, "yandex.ru", AF_INET6); + runtime.ExpectGetHostByNameError(sender, ARES_EBADFAMILY); + runtime.ExpectGetAddrError(sender, ARES_EBADFAMILY); + + runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.ExpectGetHostByNameSuccess(sender, "77.88.55.77"); + runtime.ExpectGetAddrSuccess(sender, "77.88.55.77"); + + runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); + runtime.ExpectGetHostByNameError(sender, ARES_ENOTFOUND); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + } + + Y_UNIT_TEST(DisabledIPv4) { + TCachingDnsRuntime runtime; + runtime.ResolverOptions.AllowIPv4 = false; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.SendGetHostByName(sender, "router.asus.com", AF_UNSPEC); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetHostByNameError(sender, ARES_ENODATA); + runtime.ExpectGetAddrError(sender, ARES_ENODATA); + + runtime.SendGetHostByName(sender, "router.asus.com", AF_INET); + runtime.SendGetAddr(sender, "router.asus.com", AF_INET); + runtime.ExpectGetHostByNameError(sender, ARES_EBADFAMILY); + runtime.ExpectGetAddrError(sender, ARES_EBADFAMILY); + + runtime.SendGetHostByName(sender, "router.asus.com", AF_UNSPEC); + runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC); + runtime.ExpectGetHostByNameError(sender, ARES_ENODATA); + runtime.ExpectGetAddrError(sender, ARES_ENODATA); + + runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC); + runtime.ExpectGetHostByNameError(sender, ARES_ENOTFOUND); + runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND); + } + + Y_UNIT_TEST(PoisonPill) { + TCachingDnsRuntime runtime; + runtime.Initialize(); + runtime.Start(); + + auto sender = runtime.AllocateEdgeActor(); + + runtime.SendGetHostByName(sender, "yandex.ru", AF_UNSPEC); + runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC); + runtime.Send(new IEventHandle(runtime.Resolver, sender, new TEvents::TEvPoison), 0, true); + runtime.ExpectGetHostByNameError(sender, ARES_ECANCELLED); + runtime.ExpectGetAddrError(sender, ARES_ECANCELLED); + } + +} diff --git a/library/cpp/actors/dnsresolver/dnsresolver_ondemand.cpp b/library/cpp/actors/dnsresolver/dnsresolver_ondemand.cpp index 2025162e95..da3f364601 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver_ondemand.cpp +++ b/library/cpp/actors/dnsresolver/dnsresolver_ondemand.cpp @@ -1,64 +1,64 @@ -#include "dnsresolver.h" - -#include <library/cpp/actors/core/hfunc.h> - -namespace NActors { -namespace NDnsResolver { - - class TOnDemandDnsResolver : public TActor<TOnDemandDnsResolver> { - public: - TOnDemandDnsResolver(TOnDemandDnsResolverOptions options) - : TActor(&TThis::StateWork) - , Options(std::move(options)) - { } - - static constexpr EActivityType ActorActivityType() { - return DNS_RESOLVER; - } - - private: - STRICT_STFUNC(StateWork, { - cFunc(TEvents::TEvPoison::EventType, PassAway); - fFunc(TEvDns::TEvGetHostByName::EventType, Forward); - fFunc(TEvDns::TEvGetAddr::EventType, Forward); - }); - - void Forward(STATEFN_SIG) { - ev->Rewrite(ev->GetTypeRewrite(), GetUpstream()); - TActivationContext::Send(std::move(ev)); - } - - private: - TActorId GetUpstream() { - if (Y_UNLIKELY(!CachingResolverId)) { - if (Y_LIKELY(!SimpleResolverId)) { +#include "dnsresolver.h" + +#include <library/cpp/actors/core/hfunc.h> + +namespace NActors { +namespace NDnsResolver { + + class TOnDemandDnsResolver : public TActor<TOnDemandDnsResolver> { + public: + TOnDemandDnsResolver(TOnDemandDnsResolverOptions options) + : TActor(&TThis::StateWork) + , Options(std::move(options)) + { } + + static constexpr EActivityType ActorActivityType() { + return DNS_RESOLVER; + } + + private: + STRICT_STFUNC(StateWork, { + cFunc(TEvents::TEvPoison::EventType, PassAway); + fFunc(TEvDns::TEvGetHostByName::EventType, Forward); + fFunc(TEvDns::TEvGetAddr::EventType, Forward); + }); + + void Forward(STATEFN_SIG) { + ev->Rewrite(ev->GetTypeRewrite(), GetUpstream()); + TActivationContext::Send(std::move(ev)); + } + + private: + TActorId GetUpstream() { + if (Y_UNLIKELY(!CachingResolverId)) { + if (Y_LIKELY(!SimpleResolverId)) { SimpleResolverId = RegisterWithSameMailbox(CreateSimpleDnsResolver(Options)); - } + } CachingResolverId = RegisterWithSameMailbox(CreateCachingDnsResolver(SimpleResolverId, Options)); - } - return CachingResolverId; - } - - void PassAway() override { - if (CachingResolverId) { - Send(CachingResolverId, new TEvents::TEvPoison); - CachingResolverId = { }; - } - if (SimpleResolverId) { - Send(SimpleResolverId, new TEvents::TEvPoison); - SimpleResolverId = { }; - } - } - - private: - TOnDemandDnsResolverOptions Options; - TActorId SimpleResolverId; - TActorId CachingResolverId; - }; - - IActor* CreateOnDemandDnsResolver(TOnDemandDnsResolverOptions options) { - return new TOnDemandDnsResolver(std::move(options)); - } - -} // namespace NDnsResolver -} // namespace NActors + } + return CachingResolverId; + } + + void PassAway() override { + if (CachingResolverId) { + Send(CachingResolverId, new TEvents::TEvPoison); + CachingResolverId = { }; + } + if (SimpleResolverId) { + Send(SimpleResolverId, new TEvents::TEvPoison); + SimpleResolverId = { }; + } + } + + private: + TOnDemandDnsResolverOptions Options; + TActorId SimpleResolverId; + TActorId CachingResolverId; + }; + + IActor* CreateOnDemandDnsResolver(TOnDemandDnsResolverOptions options) { + return new TOnDemandDnsResolver(std::move(options)); + } + +} // namespace NDnsResolver +} // namespace NActors diff --git a/library/cpp/actors/dnsresolver/dnsresolver_ondemand_ut.cpp b/library/cpp/actors/dnsresolver/dnsresolver_ondemand_ut.cpp index 2758484552..ee6b4113f0 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver_ondemand_ut.cpp +++ b/library/cpp/actors/dnsresolver/dnsresolver_ondemand_ut.cpp @@ -1,24 +1,24 @@ -#include "dnsresolver.h" - -#include <library/cpp/actors/testlib/test_runtime.h> -#include <library/cpp/testing/unittest/registar.h> - -using namespace NActors; -using namespace NActors::NDnsResolver; - -Y_UNIT_TEST_SUITE(OnDemandDnsResolver) { - - Y_UNIT_TEST(ResolveLocalHost) { - TTestActorRuntimeBase runtime; - runtime.Initialize(); - auto sender = runtime.AllocateEdgeActor(); - auto resolver = runtime.Register(CreateOnDemandDnsResolver()); - runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("localhost", AF_UNSPEC)), - 0, true); - auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); - size_t addrs = ev->Get()->AddrsV4.size() + ev->Get()->AddrsV6.size(); - UNIT_ASSERT_C(addrs > 0, "Got " << addrs << " addresses"); - } - -} +#include "dnsresolver.h" + +#include <library/cpp/actors/testlib/test_runtime.h> +#include <library/cpp/testing/unittest/registar.h> + +using namespace NActors; +using namespace NActors::NDnsResolver; + +Y_UNIT_TEST_SUITE(OnDemandDnsResolver) { + + Y_UNIT_TEST(ResolveLocalHost) { + TTestActorRuntimeBase runtime; + runtime.Initialize(); + auto sender = runtime.AllocateEdgeActor(); + auto resolver = runtime.Register(CreateOnDemandDnsResolver()); + runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("localhost", AF_UNSPEC)), + 0, true); + auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); + size_t addrs = ev->Get()->AddrsV4.size() + ev->Get()->AddrsV6.size(); + UNIT_ASSERT_C(addrs > 0, "Got " << addrs << " addresses"); + } + +} diff --git a/library/cpp/actors/dnsresolver/dnsresolver_ut.cpp b/library/cpp/actors/dnsresolver/dnsresolver_ut.cpp index 0c343a805c..86d2ae7a1c 100644 --- a/library/cpp/actors/dnsresolver/dnsresolver_ut.cpp +++ b/library/cpp/actors/dnsresolver/dnsresolver_ut.cpp @@ -1,98 +1,98 @@ -#include "dnsresolver.h" - -#include <library/cpp/actors/testlib/test_runtime.h> -#include <library/cpp/testing/unittest/registar.h> -#include <util/string/builder.h> - -#include <ares.h> - -using namespace NActors; -using namespace NActors::NDnsResolver; - -Y_UNIT_TEST_SUITE(DnsResolver) { - - struct TSilentUdpServer { - TInetDgramSocket Socket; - ui16 Port; - - TSilentUdpServer() { - TSockAddrInet addr("127.0.0.1", 0); - int err = Socket.Bind(&addr); - Y_VERIFY(err == 0, "Cannot bind a udp socket"); - Port = addr.GetPort(); - } - }; - - Y_UNIT_TEST(ResolveLocalHost) { - TTestActorRuntimeBase runtime; - runtime.Initialize(); - auto sender = runtime.AllocateEdgeActor(); - auto resolver = runtime.Register(CreateSimpleDnsResolver()); - runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("localhost", AF_UNSPEC)), - 0, true); - auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); - size_t addrs = ev->Get()->AddrsV4.size() + ev->Get()->AddrsV6.size(); - UNIT_ASSERT_C(addrs > 0, "Got " << addrs << " addresses"); - } - - Y_UNIT_TEST(ResolveYandexRu) { - TTestActorRuntimeBase runtime; - runtime.Initialize(); - auto sender = runtime.AllocateEdgeActor(); - auto resolver = runtime.Register(CreateSimpleDnsResolver()); - runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("yandex.ru", AF_UNSPEC)), - 0, true); - auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); - size_t addrs = ev->Get()->AddrsV4.size() + ev->Get()->AddrsV6.size(); - UNIT_ASSERT_C(addrs > 0, "Got " << addrs << " addresses"); - } - - Y_UNIT_TEST(GetAddrYandexRu) { - TTestActorRuntimeBase runtime; - runtime.Initialize(); - auto sender = runtime.AllocateEdgeActor(); - auto resolver = runtime.Register(CreateSimpleDnsResolver()); - - runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetAddr("yandex.ru", AF_UNSPEC)), - 0, true); - auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetAddrResult>(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); - UNIT_ASSERT_C(ev->Get()->IsV4() || ev->Get()->IsV6(), "Expect v4 or v6 address"); - } - - Y_UNIT_TEST(ResolveTimeout) { - TSilentUdpServer server; - TTestActorRuntimeBase runtime; - runtime.Initialize(); - auto sender = runtime.AllocateEdgeActor(); - TSimpleDnsResolverOptions options; - options.Timeout = TDuration::MilliSeconds(250); - options.Attempts = 2; - options.Servers.emplace_back(TStringBuilder() << "127.0.0.1:" << server.Port); - auto resolver = runtime.Register(CreateSimpleDnsResolver(options)); - runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("timeout.yandex.ru", AF_INET)), - 0, true); - auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, ARES_ETIMEOUT, ev->Get()->ErrorText); - } - - Y_UNIT_TEST(ResolveGracefulStop) { - TSilentUdpServer server; - TTestActorRuntimeBase runtime; - runtime.Initialize(); - auto sender = runtime.AllocateEdgeActor(); - TSimpleDnsResolverOptions options; - options.Timeout = TDuration::Seconds(5); - options.Attempts = 5; - options.Servers.emplace_back(TStringBuilder() << "127.0.0.1:" << server.Port); - auto resolver = runtime.Register(CreateSimpleDnsResolver(options)); - runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("timeout.yandex.ru", AF_INET)), - 0, true); - runtime.Send(new IEventHandle(resolver, sender, new TEvents::TEvPoison), 0, true); - auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); - UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, ARES_ECANCELLED, ev->Get()->ErrorText); - } - -} +#include "dnsresolver.h" + +#include <library/cpp/actors/testlib/test_runtime.h> +#include <library/cpp/testing/unittest/registar.h> +#include <util/string/builder.h> + +#include <ares.h> + +using namespace NActors; +using namespace NActors::NDnsResolver; + +Y_UNIT_TEST_SUITE(DnsResolver) { + + struct TSilentUdpServer { + TInetDgramSocket Socket; + ui16 Port; + + TSilentUdpServer() { + TSockAddrInet addr("127.0.0.1", 0); + int err = Socket.Bind(&addr); + Y_VERIFY(err == 0, "Cannot bind a udp socket"); + Port = addr.GetPort(); + } + }; + + Y_UNIT_TEST(ResolveLocalHost) { + TTestActorRuntimeBase runtime; + runtime.Initialize(); + auto sender = runtime.AllocateEdgeActor(); + auto resolver = runtime.Register(CreateSimpleDnsResolver()); + runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("localhost", AF_UNSPEC)), + 0, true); + auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); + size_t addrs = ev->Get()->AddrsV4.size() + ev->Get()->AddrsV6.size(); + UNIT_ASSERT_C(addrs > 0, "Got " << addrs << " addresses"); + } + + Y_UNIT_TEST(ResolveYandexRu) { + TTestActorRuntimeBase runtime; + runtime.Initialize(); + auto sender = runtime.AllocateEdgeActor(); + auto resolver = runtime.Register(CreateSimpleDnsResolver()); + runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("yandex.ru", AF_UNSPEC)), + 0, true); + auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); + size_t addrs = ev->Get()->AddrsV4.size() + ev->Get()->AddrsV6.size(); + UNIT_ASSERT_C(addrs > 0, "Got " << addrs << " addresses"); + } + + Y_UNIT_TEST(GetAddrYandexRu) { + TTestActorRuntimeBase runtime; + runtime.Initialize(); + auto sender = runtime.AllocateEdgeActor(); + auto resolver = runtime.Register(CreateSimpleDnsResolver()); + + runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetAddr("yandex.ru", AF_UNSPEC)), + 0, true); + auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetAddrResult>(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, 0, ev->Get()->ErrorText); + UNIT_ASSERT_C(ev->Get()->IsV4() || ev->Get()->IsV6(), "Expect v4 or v6 address"); + } + + Y_UNIT_TEST(ResolveTimeout) { + TSilentUdpServer server; + TTestActorRuntimeBase runtime; + runtime.Initialize(); + auto sender = runtime.AllocateEdgeActor(); + TSimpleDnsResolverOptions options; + options.Timeout = TDuration::MilliSeconds(250); + options.Attempts = 2; + options.Servers.emplace_back(TStringBuilder() << "127.0.0.1:" << server.Port); + auto resolver = runtime.Register(CreateSimpleDnsResolver(options)); + runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("timeout.yandex.ru", AF_INET)), + 0, true); + auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, ARES_ETIMEOUT, ev->Get()->ErrorText); + } + + Y_UNIT_TEST(ResolveGracefulStop) { + TSilentUdpServer server; + TTestActorRuntimeBase runtime; + runtime.Initialize(); + auto sender = runtime.AllocateEdgeActor(); + TSimpleDnsResolverOptions options; + options.Timeout = TDuration::Seconds(5); + options.Attempts = 5; + options.Servers.emplace_back(TStringBuilder() << "127.0.0.1:" << server.Port); + auto resolver = runtime.Register(CreateSimpleDnsResolver(options)); + runtime.Send(new IEventHandle(resolver, sender, new TEvDns::TEvGetHostByName("timeout.yandex.ru", AF_INET)), + 0, true); + runtime.Send(new IEventHandle(resolver, sender, new TEvents::TEvPoison), 0, true); + auto ev = runtime.GrabEdgeEventRethrow<TEvDns::TEvGetHostByNameResult>(sender); + UNIT_ASSERT_VALUES_EQUAL_C(ev->Get()->Status, ARES_ECANCELLED, ev->Get()->ErrorText); + } + +} diff --git a/library/cpp/actors/dnsresolver/ut/ya.make b/library/cpp/actors/dnsresolver/ut/ya.make index ad936bdacd..2af5f5f260 100644 --- a/library/cpp/actors/dnsresolver/ut/ya.make +++ b/library/cpp/actors/dnsresolver/ut/ya.make @@ -1,20 +1,20 @@ -UNITTEST_FOR(library/cpp/actors/dnsresolver) - -OWNER(g:kikimr) - -PEERDIR( - library/cpp/actors/testlib -) - -SRCS( - dnsresolver_caching_ut.cpp - dnsresolver_ondemand_ut.cpp - dnsresolver_ut.cpp -) - -ADDINCL(contrib/libs/c-ares) - -TAG(ya:external) -REQUIREMENTS(network:full) - -END() +UNITTEST_FOR(library/cpp/actors/dnsresolver) + +OWNER(g:kikimr) + +PEERDIR( + library/cpp/actors/testlib +) + +SRCS( + dnsresolver_caching_ut.cpp + dnsresolver_ondemand_ut.cpp + dnsresolver_ut.cpp +) + +ADDINCL(contrib/libs/c-ares) + +TAG(ya:external) +REQUIREMENTS(network:full) + +END() diff --git a/library/cpp/actors/dnsresolver/ya.make b/library/cpp/actors/dnsresolver/ya.make index 329c56c5b3..7ee69fe4b5 100644 --- a/library/cpp/actors/dnsresolver/ya.make +++ b/library/cpp/actors/dnsresolver/ya.make @@ -1,20 +1,20 @@ -LIBRARY() - -OWNER(g:kikimr) - -SRCS( - dnsresolver.cpp - dnsresolver_caching.cpp - dnsresolver_ondemand.cpp -) - -PEERDIR( - library/cpp/actors/core - contrib/libs/c-ares -) - -ADDINCL(contrib/libs/c-ares) - -END() - -RECURSE_FOR_TESTS(ut) +LIBRARY() + +OWNER(g:kikimr) + +SRCS( + dnsresolver.cpp + dnsresolver_caching.cpp + dnsresolver_ondemand.cpp +) + +PEERDIR( + library/cpp/actors/core + contrib/libs/c-ares +) + +ADDINCL(contrib/libs/c-ares) + +END() + +RECURSE_FOR_TESTS(ut) diff --git a/library/cpp/actors/interconnect/interconnect.h b/library/cpp/actors/interconnect/interconnect.h index 225a5243fd..f885377d2d 100644 --- a/library/cpp/actors/interconnect/interconnect.h +++ b/library/cpp/actors/interconnect/interconnect.h @@ -133,7 +133,7 @@ namespace NActors { const TIntrusivePtr<TTableNameserverSetup>& setup, ui32 poolId = 0); - /** + /** * Name service which can be paired with external discovery service. * Copies information from setup on the start (table may be empty). * Handles TEvNodesInfo to change list of known nodes. @@ -147,17 +147,17 @@ namespace NActors { ui32 poolId = 0); /** - * Creates an actor that resolves host/port and replies with either: - * - * - TEvLocalNodeInfo on success - * - TEvResolveError on errors - * - * Optional defaultAddress may be used as fallback. - */ - IActor* CreateResolveActor( - const TString& host, ui16 port, ui32 nodeId, const TString& defaultAddress, - const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline); - + * Creates an actor that resolves host/port and replies with either: + * + * - TEvLocalNodeInfo on success + * - TEvResolveError on errors + * + * Optional defaultAddress may be used as fallback. + */ + IActor* CreateResolveActor( + const TString& host, ui16 port, ui32 nodeId, const TString& defaultAddress, + const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline); + inline IActor* CreateResolveActor( ui32 nodeId, const TTableNameserverSetup::TNodeInfo& nodeInfo, const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) @@ -166,14 +166,14 @@ namespace NActors { replyTo, replyFrom, deadline); } - /** - * Creates an actor that resolves host/port and replies with either: - * - * - TEvAddressInfo on success - * - TEvResolveError on errors - */ - IActor* CreateResolveActor( - const TString& host, ui16 port, - const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline); - + /** + * Creates an actor that resolves host/port and replies with either: + * + * - TEvAddressInfo on success + * - TEvResolveError on errors + */ + IActor* CreateResolveActor( + const TString& host, ui16 port, + const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline); + } diff --git a/library/cpp/actors/interconnect/interconnect_nameserver_table.cpp b/library/cpp/actors/interconnect/interconnect_nameserver_table.cpp index 43419bf70d..18f3f4aed3 100644 --- a/library/cpp/actors/interconnect/interconnect_nameserver_table.cpp +++ b/library/cpp/actors/interconnect/interconnect_nameserver_table.cpp @@ -17,7 +17,7 @@ namespace NActors { return NAMESERVICE; } - TInterconnectNameserverTable(const TIntrusivePtr<TTableNameserverSetup>& setup, ui32 /*resolvePoolId*/) + TInterconnectNameserverTable(const TIntrusivePtr<TTableNameserverSetup>& setup, ui32 /*resolvePoolId*/) : TInterconnectNameserverBase<TInterconnectNameserverTable>(&TInterconnectNameserverTable::StateFunc, setup->StaticNodeTable) , Config(setup) { diff --git a/library/cpp/actors/interconnect/interconnect_resolve.cpp b/library/cpp/actors/interconnect/interconnect_resolve.cpp index 14296194df..1f0ed84e30 100644 --- a/library/cpp/actors/interconnect/interconnect_resolve.cpp +++ b/library/cpp/actors/interconnect/interconnect_resolve.cpp @@ -1,174 +1,174 @@ -#include "interconnect.h" -#include "interconnect_address.h" -#include "events_local.h" - -#include <library/cpp/actors/core/actor_bootstrapped.h> -#include <library/cpp/actors/core/hfunc.h> -#include <library/cpp/actors/dnsresolver/dnsresolver.h> - -namespace NActors { - - using namespace NActors::NDnsResolver; - - class TInterconnectResolveActor : public TActorBootstrapped<TInterconnectResolveActor> { - public: - TInterconnectResolveActor( - const TString& host, ui16 port, ui32 nodeId, const TString& defaultAddress, - const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) - : Host(host) - , NodeId(nodeId) - , Port(port) - , DefaultAddress(defaultAddress) - , ReplyTo(replyTo) - , ReplyFrom(replyFrom) - , Deadline(deadline) - { } - - TInterconnectResolveActor( - const TString& host, ui16 port, - const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) - : Host(host) - , Port(port) - , ReplyTo(replyTo) - , ReplyFrom(replyFrom) - , Deadline(deadline) - { } - - static constexpr EActivityType ActorActivityType() { - return NAMESERVICE; - } - - void Bootstrap() { - TMaybe<TString> errorText; - if (auto addr = ExtractDefaultAddr(errorText)) { - return SendAddrAndDie(std::move(addr)); - } - - if (errorText) { - SendErrorAndDie(*errorText); - } - - auto now = TActivationContext::Now(); - if (Deadline < now) { - SendErrorAndDie("Deadline"); - return; - } - - Send(MakeDnsResolverActorId(), - new TEvDns::TEvGetAddr(Host, AF_UNSPEC), - IEventHandle::FlagTrackDelivery); - - if (Deadline != TInstant::Max()) { - Schedule(Deadline, new TEvents::TEvWakeup); - } - - Become(&TThis::StateWork); - } - - STRICT_STFUNC(StateWork, { - sFunc(TEvents::TEvWakeup, HandleTimeout); - sFunc(TEvents::TEvUndelivered, HandleUndelivered); - hFunc(TEvDns::TEvGetAddrResult, Handle); - }); - - void HandleTimeout() { - SendErrorAndDie("Deadline"); - } - - void HandleUndelivered() { - SendErrorAndDie("Dns resolver is unavailable"); - } - - void Handle(TEvDns::TEvGetAddrResult::TPtr& ev) { - if (auto addr = ExtractAddr(ev->Get())) { - return SendAddrAndDie(std::move(addr)); - } - - SendErrorAndDie(ev->Get()->ErrorText); - } - - void SendAddrAndDie(NAddr::IRemoteAddrPtr addr) { - if (NodeId) { - auto reply = new TEvLocalNodeInfo; - reply->NodeId = *NodeId; - reply->Address = std::move(addr); - TActivationContext::Send(new IEventHandle(ReplyTo, ReplyFrom, reply)); - } else { - auto reply = new TEvAddressInfo; - reply->Address = std::move(addr); - TActivationContext::Send(new IEventHandle(ReplyTo, ReplyFrom, reply)); - } - PassAway(); - } - - void SendErrorAndDie(const TString& errorText) { - auto *event = new TEvResolveError; - event->Explain = errorText; - TActivationContext::Send(new IEventHandle(ReplyTo, ReplyFrom, event)); - PassAway(); - } - - NAddr::IRemoteAddrPtr ExtractAddr(TEvDns::TEvGetAddrResult* msg) { - if (msg->Status == 0) { - if (msg->IsV6()) { - struct sockaddr_in6 sin6; - Zero(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = msg->GetAddrV6(); - sin6.sin6_port = HostToInet(Port); - return MakeHolder<NAddr::TIPv6Addr>(sin6); - } - - if (msg->IsV4()) { - return MakeHolder<NAddr::TIPv4Addr>(TIpAddress(msg->GetAddrV4().s_addr, Port)); - } - - Y_FAIL("Unexpected result address family"); - } - - return nullptr; - } - - NAddr::IRemoteAddrPtr ExtractDefaultAddr(TMaybe<TString>& errorText) { - if (DefaultAddress) { - NInterconnect::TAddress address(DefaultAddress.data(), Port); - - switch (address.GetFamily()) { - case AF_INET: - return MakeHolder<NAddr::TIPv4Addr>(*(sockaddr_in*)address.SockAddr()); - case AF_INET6: - return MakeHolder<NAddr::TIPv6Addr>(*(sockaddr_in6*)address.SockAddr()); - default: - errorText = "Unsupported default address: " + DefaultAddress; - break; - } - } - - return nullptr; - } - - private: - const TString Host; - const std::optional<ui32> NodeId; - const ui16 Port; - const TString DefaultAddress; - const TActorId ReplyTo; - const TActorId ReplyFrom; - const TInstant Deadline; - }; - - IActor* CreateResolveActor( - const TString& host, ui16 port, ui32 nodeId, const TString& defaultAddress, - const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) - { - return new TInterconnectResolveActor(host, port, nodeId, defaultAddress, replyTo, replyFrom, deadline); - } - - IActor* CreateResolveActor( - const TString& host, ui16 port, - const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) - { - return new TInterconnectResolveActor(host, port, replyTo, replyFrom, deadline); - } - -} // namespace NActors +#include "interconnect.h" +#include "interconnect_address.h" +#include "events_local.h" + +#include <library/cpp/actors/core/actor_bootstrapped.h> +#include <library/cpp/actors/core/hfunc.h> +#include <library/cpp/actors/dnsresolver/dnsresolver.h> + +namespace NActors { + + using namespace NActors::NDnsResolver; + + class TInterconnectResolveActor : public TActorBootstrapped<TInterconnectResolveActor> { + public: + TInterconnectResolveActor( + const TString& host, ui16 port, ui32 nodeId, const TString& defaultAddress, + const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) + : Host(host) + , NodeId(nodeId) + , Port(port) + , DefaultAddress(defaultAddress) + , ReplyTo(replyTo) + , ReplyFrom(replyFrom) + , Deadline(deadline) + { } + + TInterconnectResolveActor( + const TString& host, ui16 port, + const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) + : Host(host) + , Port(port) + , ReplyTo(replyTo) + , ReplyFrom(replyFrom) + , Deadline(deadline) + { } + + static constexpr EActivityType ActorActivityType() { + return NAMESERVICE; + } + + void Bootstrap() { + TMaybe<TString> errorText; + if (auto addr = ExtractDefaultAddr(errorText)) { + return SendAddrAndDie(std::move(addr)); + } + + if (errorText) { + SendErrorAndDie(*errorText); + } + + auto now = TActivationContext::Now(); + if (Deadline < now) { + SendErrorAndDie("Deadline"); + return; + } + + Send(MakeDnsResolverActorId(), + new TEvDns::TEvGetAddr(Host, AF_UNSPEC), + IEventHandle::FlagTrackDelivery); + + if (Deadline != TInstant::Max()) { + Schedule(Deadline, new TEvents::TEvWakeup); + } + + Become(&TThis::StateWork); + } + + STRICT_STFUNC(StateWork, { + sFunc(TEvents::TEvWakeup, HandleTimeout); + sFunc(TEvents::TEvUndelivered, HandleUndelivered); + hFunc(TEvDns::TEvGetAddrResult, Handle); + }); + + void HandleTimeout() { + SendErrorAndDie("Deadline"); + } + + void HandleUndelivered() { + SendErrorAndDie("Dns resolver is unavailable"); + } + + void Handle(TEvDns::TEvGetAddrResult::TPtr& ev) { + if (auto addr = ExtractAddr(ev->Get())) { + return SendAddrAndDie(std::move(addr)); + } + + SendErrorAndDie(ev->Get()->ErrorText); + } + + void SendAddrAndDie(NAddr::IRemoteAddrPtr addr) { + if (NodeId) { + auto reply = new TEvLocalNodeInfo; + reply->NodeId = *NodeId; + reply->Address = std::move(addr); + TActivationContext::Send(new IEventHandle(ReplyTo, ReplyFrom, reply)); + } else { + auto reply = new TEvAddressInfo; + reply->Address = std::move(addr); + TActivationContext::Send(new IEventHandle(ReplyTo, ReplyFrom, reply)); + } + PassAway(); + } + + void SendErrorAndDie(const TString& errorText) { + auto *event = new TEvResolveError; + event->Explain = errorText; + TActivationContext::Send(new IEventHandle(ReplyTo, ReplyFrom, event)); + PassAway(); + } + + NAddr::IRemoteAddrPtr ExtractAddr(TEvDns::TEvGetAddrResult* msg) { + if (msg->Status == 0) { + if (msg->IsV6()) { + struct sockaddr_in6 sin6; + Zero(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = msg->GetAddrV6(); + sin6.sin6_port = HostToInet(Port); + return MakeHolder<NAddr::TIPv6Addr>(sin6); + } + + if (msg->IsV4()) { + return MakeHolder<NAddr::TIPv4Addr>(TIpAddress(msg->GetAddrV4().s_addr, Port)); + } + + Y_FAIL("Unexpected result address family"); + } + + return nullptr; + } + + NAddr::IRemoteAddrPtr ExtractDefaultAddr(TMaybe<TString>& errorText) { + if (DefaultAddress) { + NInterconnect::TAddress address(DefaultAddress.data(), Port); + + switch (address.GetFamily()) { + case AF_INET: + return MakeHolder<NAddr::TIPv4Addr>(*(sockaddr_in*)address.SockAddr()); + case AF_INET6: + return MakeHolder<NAddr::TIPv6Addr>(*(sockaddr_in6*)address.SockAddr()); + default: + errorText = "Unsupported default address: " + DefaultAddress; + break; + } + } + + return nullptr; + } + + private: + const TString Host; + const std::optional<ui32> NodeId; + const ui16 Port; + const TString DefaultAddress; + const TActorId ReplyTo; + const TActorId ReplyFrom; + const TInstant Deadline; + }; + + IActor* CreateResolveActor( + const TString& host, ui16 port, ui32 nodeId, const TString& defaultAddress, + const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) + { + return new TInterconnectResolveActor(host, port, nodeId, defaultAddress, replyTo, replyFrom, deadline); + } + + IActor* CreateResolveActor( + const TString& host, ui16 port, + const TActorId& replyTo, const TActorId& replyFrom, TInstant deadline) + { + return new TInterconnectResolveActor(host, port, replyTo, replyFrom, deadline); + } + +} // namespace NActors diff --git a/library/cpp/actors/interconnect/interconnect_tcp_proxy.cpp b/library/cpp/actors/interconnect/interconnect_tcp_proxy.cpp index 7e2d8ccb94..a634fd804b 100644 --- a/library/cpp/actors/interconnect/interconnect_tcp_proxy.cpp +++ b/library/cpp/actors/interconnect/interconnect_tcp_proxy.cpp @@ -43,7 +43,7 @@ namespace NActors { PassAwayTimestamp = TActivationContext::Now() + TDuration::Seconds(15); LOG_INFO_IC("ICP01", "ready to work"); - } + } void TInterconnectProxyTCP::Registered(TActorSystem* sys, const TActorId& owner) { if (!DynamicPtr) { @@ -53,7 +53,7 @@ namespace NActors { if (const auto& mon = Common->RegisterMonPage) { TString path = Sprintf("peer%04" PRIu32, PeerNodeId); TString title = Sprintf("Peer #%04" PRIu32, PeerNodeId); - mon(path, title, sys, SelfId()); + mon(path, title, sys, SelfId()); } } @@ -508,14 +508,14 @@ namespace NActors { switch (ev->GetTypeRewrite()) { case TEvInterconnect::EvForward: if (ev->Flags & IEventHandle::FlagSubscribeOnSession) { - Send(ev->Sender, new TEvInterconnect::TEvNodeDisconnected(PeerNodeId), 0, ev->Cookie); + Send(ev->Sender, new TEvInterconnect::TEvNodeDisconnected(PeerNodeId), 0, ev->Cookie); } TActivationContext::Send(ev->ForwardOnNondelivery(TEvents::TEvUndelivered::Disconnected)); break; case TEvInterconnect::TEvConnectNode::EventType: case TEvents::TEvSubscribe::EventType: - Send(ev->Sender, new TEvInterconnect::TEvNodeDisconnected(PeerNodeId), 0, ev->Cookie); + Send(ev->Sender, new TEvInterconnect::TEvNodeDisconnected(PeerNodeId), 0, ev->Cookie); break; case TEvents::TEvUnsubscribe::EventType: diff --git a/library/cpp/actors/interconnect/interconnect_tcp_session.cpp b/library/cpp/actors/interconnect/interconnect_tcp_session.cpp index 2ded7f9f53..8767bd68ce 100644 --- a/library/cpp/actors/interconnect/interconnect_tcp_session.cpp +++ b/library/cpp/actors/interconnect/interconnect_tcp_session.cpp @@ -82,8 +82,8 @@ namespace NActors { IActor::InvokeOtherActor(*Proxy, &TInterconnectProxyTCP::UnregisterSession, this); ShutdownSocket(std::move(reason)); - for (const auto& kv : Subscribers) { - Send(kv.first, new TEvInterconnect::TEvNodeDisconnected(Proxy->PeerNodeId), 0, kv.second); + for (const auto& kv : Subscribers) { + Send(kv.first, new TEvInterconnect::TEvNodeDisconnected(Proxy->PeerNodeId), 0, kv.second); } Proxy->Metrics->SubSubscribersCount(Subscribers.size()); Subscribers.clear(); @@ -186,13 +186,13 @@ namespace NActors { void TInterconnectSessionTCP::Subscribe(STATEFN_SIG) { LOG_DEBUG_IC_SESSION("ICS04", "subscribe for session state for %s", ev->Sender.ToString().data()); - const auto [it, inserted] = Subscribers.emplace(ev->Sender, ev->Cookie); + const auto [it, inserted] = Subscribers.emplace(ev->Sender, ev->Cookie); if (inserted) { Proxy->Metrics->IncSubscribersCount(); - } else { - it->second = ev->Cookie; + } else { + it->second = ev->Cookie; } - Send(ev->Sender, new TEvInterconnect::TEvNodeConnected(Proxy->PeerNodeId), 0, ev->Cookie); + Send(ev->Sender, new TEvInterconnect::TEvNodeConnected(Proxy->PeerNodeId), 0, ev->Cookie); } void TInterconnectSessionTCP::Unsubscribe(STATEFN_SIG) { diff --git a/library/cpp/actors/interconnect/interconnect_tcp_session.h b/library/cpp/actors/interconnect/interconnect_tcp_session.h index 7fc00dbcc5..76357187b0 100644 --- a/library/cpp/actors/interconnect/interconnect_tcp_session.h +++ b/library/cpp/actors/interconnect/interconnect_tcp_session.h @@ -477,7 +477,7 @@ namespace NActors { ui32 SendBufferSize; ui64 InflightDataAmount = 0; - std::unordered_map<TActorId, ui64, TActorId::THash> Subscribers; + std::unordered_map<TActorId, ui64, TActorId::THash> Subscribers; // time at which we want to send confirmation packet even if there was no outgoing data ui64 UnconfirmedBytes = 0; diff --git a/library/cpp/actors/interconnect/mock/ic_mock.cpp b/library/cpp/actors/interconnect/mock/ic_mock.cpp index 884503e602..275a02e551 100644 --- a/library/cpp/actors/interconnect/mock/ic_mock.cpp +++ b/library/cpp/actors/interconnect/mock/ic_mock.cpp @@ -95,7 +95,7 @@ namespace NActors { class TProxyMockActor : public TActor<TProxyMockActor> { class TSessionMockActor : public TActor<TSessionMockActor> { - std::map<TActorId, ui64> Subscribers; + std::map<TActorId, ui64> Subscribers; TProxyMockActor* const Proxy; std::deque<std::unique_ptr<IEventHandle>> Queue; @@ -113,8 +113,8 @@ namespace NActors { for (auto&& ev : std::exchange(Queue, {})) { TActivationContext::Send(ev->ForwardOnNondelivery(TEvents::TEvUndelivered::Disconnected)); } - for (const auto& kv : Subscribers) { - Send(kv.first, new TEvInterconnect::TEvNodeDisconnected(Proxy->PeerNodeId), 0, kv.second); + for (const auto& kv : Subscribers) { + Send(kv.first, new TEvInterconnect::TEvNodeDisconnected(Proxy->PeerNodeId), 0, kv.second); } Y_VERIFY(Proxy->Session == this); Proxy->Session = nullptr; @@ -123,7 +123,7 @@ namespace NActors { void HandleForward(TAutoPtr<IEventHandle> ev) { if (ev->Flags & IEventHandle::FlagSubscribeOnSession) { - Subscribe(ev->Sender, ev->Cookie); + Subscribe(ev->Sender, ev->Cookie); } if (Queue.empty()) { TActivationContext::Send(new IEventHandle(EvRam, 0, SelfId(), {}, {}, 0)); @@ -140,11 +140,11 @@ namespace NActors { } void Handle(TEvInterconnect::TEvConnectNode::TPtr ev) { - Subscribe(ev->Sender, ev->Cookie); + Subscribe(ev->Sender, ev->Cookie); } void Handle(TEvents::TEvSubscribe::TPtr ev) { - Subscribe(ev->Sender, ev->Cookie); + Subscribe(ev->Sender, ev->Cookie); } void Handle(TEvents::TEvUnsubscribe::TPtr ev) { @@ -165,9 +165,9 @@ namespace NActors { ) private: - void Subscribe(const TActorId& actorId, ui64 cookie) { - Subscribers[actorId] = cookie; - Send(actorId, new TEvInterconnect::TEvNodeConnected(Proxy->PeerNodeId), 0, cookie); + void Subscribe(const TActorId& actorId, ui64 cookie) { + Subscribers[actorId] = cookie; + Send(actorId, new TEvInterconnect::TEvNodeConnected(Proxy->PeerNodeId), 0, cookie); } }; diff --git a/library/cpp/actors/interconnect/ut/lib/ic_test_cluster.h b/library/cpp/actors/interconnect/ut/lib/ic_test_cluster.h index 2b6d27cd3f..5b3c8f1dcc 100644 --- a/library/cpp/actors/interconnect/ut/lib/ic_test_cluster.h +++ b/library/cpp/actors/interconnect/ut/lib/ic_test_cluster.h @@ -25,7 +25,7 @@ private: THashMap<ui32, THolder<TNode>> Nodes; TList<TTrafficInterrupter> interrupters; NActors::TChannelsConfig ChannelsConfig; - TPortManager PortManager; + TPortManager PortManager; public: TTestICCluster(ui32 numNodes = 1, NActors::TChannelsConfig channelsConfig = NActors::TChannelsConfig(), @@ -38,7 +38,7 @@ public: THashMap<ui32, THashMap<ui32, ui16>> specificNodePortMap; for (ui32 i = 1; i <= NumNodes; ++i) { - nodeToPortMap.emplace(i, PortManager.GetPort()); + nodeToPortMap.emplace(i, PortManager.GetPort()); } if (tiSettings) { @@ -48,7 +48,7 @@ public: for (auto& item : nodeToPortMap) { nodeId = item.first; listenPort = item.second; - forwardPort = PortManager.GetPort(); + forwardPort = PortManager.GetPort(); specificNodePortMap[nodeId] = nodeToPortMap; specificNodePortMap[nodeId].at(nodeId) = forwardPort; diff --git a/library/cpp/actors/interconnect/ut/lib/node.h b/library/cpp/actors/interconnect/ut/lib/node.h index ff30b1445e..12087fbe32 100644 --- a/library/cpp/actors/interconnect/ut/lib/node.h +++ b/library/cpp/actors/interconnect/ut/lib/node.h @@ -4,7 +4,7 @@ #include <library/cpp/actors/core/executor_pool_basic.h> #include <library/cpp/actors/core/scheduler_basic.h> #include <library/cpp/actors/core/mailbox.h> -#include <library/cpp/actors/dnsresolver/dnsresolver.h> +#include <library/cpp/actors/dnsresolver/dnsresolver.h> #include <library/cpp/actors/interconnect/interconnect_tcp_server.h> #include <library/cpp/actors/interconnect/interconnect_tcp_proxy.h> @@ -91,7 +91,7 @@ public: for (ui32 i = 1; i <= numNodes; ++i) { names->StaticNodeTable[i] = TTableNameserverSetup::TNodeInfo(address, address, nodeToPort.at(i)); } - setup.LocalServices.emplace_back( + setup.LocalServices.emplace_back( NDnsResolver::MakeDnsResolverActorId(), TActorSetupCmd( NDnsResolver::CreateOnDemandDnsResolver(), @@ -111,7 +111,7 @@ public: } ~TNode() { - ActorSystem->Stop(); + ActorSystem->Stop(); } bool Send(const TActorId& recipient, IEventBase* ev) { diff --git a/library/cpp/actors/interconnect/ya.make b/library/cpp/actors/interconnect/ya.make index 60d29b0fc0..01a57c3aa2 100644 --- a/library/cpp/actors/interconnect/ya.make +++ b/library/cpp/actors/interconnect/ya.make @@ -33,7 +33,7 @@ SRCS( interconnect_nameserver_table.cpp interconnect_proxy_wrapper.cpp interconnect_proxy_wrapper.h - interconnect_resolve.cpp + interconnect_resolve.cpp interconnect_stream.cpp interconnect_stream.h interconnect_tcp_input_session.cpp @@ -76,7 +76,7 @@ PEERDIR( contrib/libs/openssl library/cpp/actors/core library/cpp/actors/dnscachelib - library/cpp/actors/dnsresolver + library/cpp/actors/dnsresolver library/cpp/actors/helpers library/cpp/actors/prof library/cpp/actors/protos diff --git a/library/cpp/actors/testlib/test_runtime.cpp b/library/cpp/actors/testlib/test_runtime.cpp index 6fa25b9965..cf93742d21 100644 --- a/library/cpp/actors/testlib/test_runtime.cpp +++ b/library/cpp/actors/testlib/test_runtime.cpp @@ -58,7 +58,7 @@ namespace NActors { TTestActorRuntimeBase::TNodeDataBase::TNodeDataBase() { ActorSystemTimestamp = nullptr; - ActorSystemMonotonic = nullptr; + ActorSystemMonotonic = nullptr; } void TTestActorRuntimeBase::TNodeDataBase::Stop() { @@ -210,10 +210,10 @@ namespace NActors { return Scheduled.empty(); } - TInstant TEventMailBox::GetFirstScheduleDeadline() const { - return Scheduled.begin()->Deadline; - } - + TInstant TEventMailBox::GetFirstScheduleDeadline() const { + return Scheduled.begin()->Deadline; + } + ui64 TEventMailBox::GetSentEventCount() const { return Sent.size(); } @@ -245,7 +245,7 @@ namespace NActors { void Prepare(TActorSystem *actorSystem, volatile ui64 *currentTimestamp, volatile ui64 *currentMonotonic) override { Y_UNUSED(actorSystem); Node->ActorSystemTimestamp = currentTimestamp; - Node->ActorSystemMonotonic = currentMonotonic; + Node->ActorSystemMonotonic = currentMonotonic; } void PrepareSchedules(NSchedulerQueue::TReader **readers, ui32 scheduleReadersCount) override { @@ -299,8 +299,8 @@ namespace NActors { void Schedule(TMonotonic deadline, TAutoPtr<IEventHandle> ev, ISchedulerCookie *cookie, TWorkerId workerId) override { DoSchedule(TInstant::FromValue(deadline.GetValue()), ev, cookie, workerId); - } - + } + void Schedule(TDuration delay, TAutoPtr<IEventHandle> ev, ISchedulerCookie *cookie, TWorkerId workerId) override { TInstant deadline = Runtime->GetTimeProvider()->Now() + delay; DoSchedule(deadline, ev, cookie, workerId); @@ -467,7 +467,7 @@ namespace NActors { , LocalId(0) , DispatchCyclesCount(0) , DispatchedEventsCount(0) - , NeedMonitoring(false) + , NeedMonitoring(false) , RandomProvider(CreateDeterministicRandomProvider(DefaultRandomSeed)) , TimeProvider(new TTimeProvider(*this)) , ShouldContinue() @@ -490,7 +490,7 @@ namespace NActors { node->LogSettings = new NActors::NLog::TSettings(loggerActorId, 410 /* NKikimrServices::LOGGER */, NActors::NLog::PRI_WARN, NActors::NLog::PRI_WARN, 0); node->LogSettings->SetAllowDrop(false); - node->LogSettings->SetThrottleDelay(TDuration::Zero()); + node->LogSettings->SetThrottleDelay(TDuration::Zero()); node->DynamicCounters = new NMonitoring::TDynamicCounters; InitNodeImpl(node, nodeIndex); @@ -572,9 +572,9 @@ namespace NActors { bool TTestActorRuntimeBase::NopFilterFunc(TTestActorRuntimeBase& runtime, TAutoPtr<IEventHandle>& event, TDuration delay, TInstant& deadline) { Y_UNUSED(runtime); - Y_UNUSED(delay); + Y_UNUSED(delay); Y_UNUSED(event); - Y_UNUSED(deadline); + Y_UNUSED(deadline); return true; } @@ -843,14 +843,14 @@ namespace NActors { } ui32 TTestActorRuntimeBase::InterconnectPoolId() const { - if (UseRealThreads && NSan::TSanIsOn()) { - // Interconnect coroutines may move across threads - // Use a special single-threaded pool to avoid that - return 4; - } - return 0; - } - + if (UseRealThreads && NSan::TSanIsOn()) { + // Interconnect coroutines may move across threads + // Use a special single-threaded pool to avoid that + return 4; + } + return 0; + } + TString TTestActorRuntimeBase::GetTempDir() { if (!TmpDir) TmpDir.Reset(new TTempDir()); @@ -967,7 +967,7 @@ namespace NActors { Y_VERIFY(nodeIndex < NodeCount); TActorId edgeActor = Register(new TEdgeActor(this), nodeIndex); EdgeActors.insert(edgeActor); - EdgeActorByMailbox[TEventMailboxId(edgeActor.NodeId(), edgeActor.Hint())] = edgeActor; + EdgeActorByMailbox[TEventMailboxId(edgeActor.NodeId(), edgeActor.Hint())] = edgeActor; return edgeActor; } @@ -1161,15 +1161,15 @@ namespace NActors { if (mbox.second->IsActive(TInstant::MicroSeconds(CurrentTimestamp))) { bool isEdgeMailbox = false; - if (EdgeActorByMailbox.FindPtr(TEventMailboxId(mbox.first.NodeId, mbox.first.Hint))) { - isEdgeMailbox = true; - TEventsList events; - mbox.second->Capture(events); - for (auto& ev : events) { - TInverseGuard<TMutex> inverseGuard(Mutex); - ObserverFunc(*this, ev); + if (EdgeActorByMailbox.FindPtr(TEventMailboxId(mbox.first.NodeId, mbox.first.Hint))) { + isEdgeMailbox = true; + TEventsList events; + mbox.second->Capture(events); + for (auto& ev : events) { + TInverseGuard<TMutex> inverseGuard(Mutex); + ObserverFunc(*this, ev); } - mbox.second->PushFront(events); + mbox.second->PushFront(events); } if (!isEdgeMailbox) { @@ -1246,8 +1246,8 @@ namespace NActors { } } - if (localContext.FinalEventFound) { - return true; + if (localContext.FinalEventFound) { + return true; } if (!localContext.FoundNonEmptyMailboxes.empty()) @@ -1287,8 +1287,8 @@ namespace NActors { inspectScheduledEventsAt = dispatchTime + scheduledEventsInspectInterval; bool isEmpty = true; TMaybe<TInstant> nearestMailboxDeadline; - TVector<TIntrusivePtr<TEventMailBox>> nextScheduleMboxes; - TMaybe<TInstant> nextScheduleDeadline; + TVector<TIntrusivePtr<TEventMailBox>> nextScheduleMboxes; + TMaybe<TInstant> nextScheduleDeadline; for (auto& mbox : currentMailboxes) { if (!mbox.second->IsActive(TInstant::MicroSeconds(CurrentTimestamp))) { if (!nearestMailboxDeadline.Defined() || *nearestMailboxDeadline.Get() > mbox.second->GetInactiveUntil()) { @@ -1301,29 +1301,29 @@ namespace NActors { if (mbox.second->IsScheduledEmpty()) continue; - auto firstScheduleDeadline = mbox.second->GetFirstScheduleDeadline(); - if (!nextScheduleDeadline || firstScheduleDeadline < *nextScheduleDeadline) { - nextScheduleMboxes.clear(); - nextScheduleMboxes.emplace_back(mbox.second); - nextScheduleDeadline = firstScheduleDeadline; - } else if (firstScheduleDeadline == *nextScheduleDeadline) { - nextScheduleMboxes.emplace_back(mbox.second); - } - } - - for (const auto& nextScheduleMbox : nextScheduleMboxes) { + auto firstScheduleDeadline = mbox.second->GetFirstScheduleDeadline(); + if (!nextScheduleDeadline || firstScheduleDeadline < *nextScheduleDeadline) { + nextScheduleMboxes.clear(); + nextScheduleMboxes.emplace_back(mbox.second); + nextScheduleDeadline = firstScheduleDeadline; + } else if (firstScheduleDeadline == *nextScheduleDeadline) { + nextScheduleMboxes.emplace_back(mbox.second); + } + } + + for (const auto& nextScheduleMbox : nextScheduleMboxes) { TEventsList selectedEvents; TScheduledEventsList capturedScheduledEvents; - nextScheduleMbox->CaptureScheduled(capturedScheduledEvents); + nextScheduleMbox->CaptureScheduled(capturedScheduledEvents); ScheduledEventsSelectorFunc(*this, capturedScheduledEvents, selectedEvents); - nextScheduleMbox->PushScheduled(capturedScheduledEvents); + nextScheduleMbox->PushScheduled(capturedScheduledEvents); for (auto& event : selectedEvents) { if (verbose && (BlockedOutput.find(event->Sender) == BlockedOutput.end())) { Cerr << "Selected scheduled event at " << TInstant::MicroSeconds(CurrentTimestamp) << ", "; PrintEvent(event, this); } - nextScheduleMbox->Send(event); + nextScheduleMbox->Send(event); isEmpty = false; } } @@ -1371,10 +1371,10 @@ namespace NActors { while (context) { for (const auto& finalEvent : context->Options->FinalEvents) { if (finalEvent.EventCheck(ev)) { - auto& freq = context->FinalEventFrequency[&finalEvent]; - if (++freq >= finalEvent.RequiredCount) { - context->FinalEventFound = true; - } + auto& freq = context->FinalEventFrequency[&finalEvent]; + if (++freq >= finalEvent.RequiredCount) { + context->FinalEventFound = true; + } } } @@ -1424,15 +1424,15 @@ namespace NActors { void TTestActorRuntimeBase::WaitForEdgeEvents(TEventFilter filter, const TSet<TActorId>& edgeFilter, TDuration simTimeout) { TGuard<TMutex> guard(Mutex); ui32 dispatchCount = 0; - if (!edgeFilter.empty()) { - for (auto edgeActor : edgeFilter) { + if (!edgeFilter.empty()) { + for (auto edgeActor : edgeFilter) { Y_VERIFY(EdgeActors.contains(edgeActor), "%s is not an edge actor", ToString(edgeActor).data()); - } - } + } + } const TSet<TActorId>& edgeActors = edgeFilter.empty() ? EdgeActors : edgeFilter; - TInstant deadline = TInstant::MicroSeconds(CurrentTimestamp) + simTimeout; + TInstant deadline = TInstant::MicroSeconds(CurrentTimestamp) + simTimeout; for (;;) { - for (auto edgeActor : edgeActors) { + for (auto edgeActor : edgeActors) { TEventsList events; auto& mbox = GetMailbox(edgeActor.NodeId(), edgeActor.Hint()); bool foundEvent = false; @@ -1451,7 +1451,7 @@ namespace NActors { ++dispatchCount; { - if (!DispatchEventsInternal(TDispatchOptions(), deadline)) { + if (!DispatchEventsInternal(TDispatchOptions(), deadline)) { return; // Timed out; event was not found } } @@ -1522,8 +1522,8 @@ namespace NActors { return node->DynamicCounters; } - void TTestActorRuntimeBase::SetupMonitoring() { - NeedMonitoring = true; + void TTestActorRuntimeBase::SetupMonitoring() { + NeedMonitoring = true; } void TTestActorRuntimeBase::SendInternal(IEventHandle* ev, ui32 nodeIndex, bool viaActorSystem) { @@ -1612,13 +1612,13 @@ namespace NActors { setup->NodeId = FirstNodeId + nodeIndex; if (UseRealThreads) { - setup->ExecutorsCount = 5; - setup->Executors.Reset(new TAutoPtr<IExecutorPool>[5]); + setup->ExecutorsCount = 5; + setup->Executors.Reset(new TAutoPtr<IExecutorPool>[5]); setup->Executors[0].Reset(new TBasicExecutorPool(0, 2, 20)); setup->Executors[1].Reset(new TBasicExecutorPool(1, 2, 20)); setup->Executors[2].Reset(new TIOExecutorPool(2, 1)); setup->Executors[3].Reset(new TBasicExecutorPool(3, 2, 20)); - setup->Executors[4].Reset(new TBasicExecutorPool(4, 1, 20)); + setup->Executors[4].Reset(new TBasicExecutorPool(4, 1, 20)); setup->Scheduler.Reset(new TBasicSchedulerThread(TSchedulerConfig(512, 100))); } else { setup->ExecutorsCount = 1; @@ -1652,14 +1652,14 @@ namespace NActors { common->MonCounters = interconnectCounters; common->TechnicalSelfHostName = "::1"; - if (!UseRealThreads) { + if (!UseRealThreads) { common->Settings.DeadPeer = TDuration::Max(); common->Settings.CloseOnIdle = TDuration::Max(); common->Settings.PingPeriod = TDuration::Max(); common->Settings.ForceConfirmPeriod = TDuration::Max(); common->Settings.Handshake = TDuration::Max(); - } - + } + common->ClusterUUID = ClusterUUID; common->AcceptUUID = {ClusterUUID}; @@ -1677,7 +1677,7 @@ namespace NActors { } setup->Interconnect.ProxyWrapperFactory = CreateProxyWrapperFactory(common, InterconnectPoolId(), &InterconnectMock); - + if (UseRealInterconnect) { setup->LocalServices.emplace_back(MakePollerActorId(), NActors::TActorSetupCmd(CreatePollerActor(), NActors::TMailboxType::Simple, InterconnectPoolId())); diff --git a/library/cpp/actors/testlib/test_runtime.h b/library/cpp/actors/testlib/test_runtime.h index 26e3b45c98..387b386df3 100644 --- a/library/cpp/actors/testlib/test_runtime.h +++ b/library/cpp/actors/testlib/test_runtime.h @@ -16,7 +16,7 @@ #include <util/datetime/base.h> #include <util/folder/tempdir.h> #include <util/generic/deque.h> -#include <util/generic/hash.h> +#include <util/generic/hash.h> #include <util/generic/noncopyable.h> #include <util/generic/ptr.h> #include <util/generic/queue.h> @@ -62,12 +62,12 @@ namespace NActors { return (NodeId == other.NodeId) && (Hint == other.Hint); } - struct THash { - ui64 operator()(const TEventMailboxId& mboxId) const noexcept { - return mboxId.NodeId * 31ULL + mboxId.Hint; - } - }; - + struct THash { + ui64 operator()(const TEventMailboxId& mboxId) const noexcept { + return mboxId.NodeId * 31ULL + mboxId.Hint; + } + }; + ui32 NodeId; ui32 Hint; }; @@ -150,7 +150,7 @@ namespace NActors { TInstant GetInactiveUntil() const; void Schedule(const TScheduledEventQueueItem& item); bool IsScheduledEmpty() const; - TInstant GetFirstScheduleDeadline() const; + TInstant GetFirstScheduleDeadline() const; ui64 GetSentEventCount() const; private: @@ -164,7 +164,7 @@ namespace NActors { #endif }; - typedef THashMap<TEventMailboxId, TIntrusivePtr<TEventMailBox>, TEventMailboxId::THash> TEventMailBoxList; + typedef THashMap<TEventMailboxId, TIntrusivePtr<TEventMailBox>, TEventMailboxId::THash> TEventMailBoxList; class TEmptyEventQueueException : public yexception { public: @@ -237,7 +237,7 @@ namespace NActors { ui32 GetNodeId(ui32 index = 0) const; ui32 GetNodeCount() const; ui64 AllocateLocalId(); - ui32 InterconnectPoolId() const; + ui32 InterconnectPoolId() const; TString GetTempDir(); TActorId Register(IActor* actor, ui32 nodeIndex = 0, ui32 poolId = 0, TMailboxType::EType mailboxType = TMailboxType::Simple, ui64 revolvingCounter = 0, @@ -268,7 +268,7 @@ namespace NActors { void EnableScheduleForActor(const TActorId& actorId, bool allow = true); bool IsScheduleForActorEnabled(const TActorId& actorId) const; TIntrusivePtr<NMonitoring::TDynamicCounters> GetDynamicCounters(ui32 nodeIndex = 0); - void SetupMonitoring(); + void SetupMonitoring(); template<typename T> void AppendToLogSettings(NLog::EComponent minVal, NLog::EComponent maxVal, T func) { @@ -303,7 +303,7 @@ namespace NActors { } return false; - }, {}, simTimeout); + }, {}, simTimeout); if (simTimeout == TDuration::Max()) Y_VERIFY(handle); @@ -315,44 +315,44 @@ namespace NActors { } } - template<class TEvent> - typename TEvent::TPtr GrabEdgeEventIf( + template<class TEvent> + typename TEvent::TPtr GrabEdgeEventIf( const TSet<TActorId>& edgeFilter, - const std::function<bool(const typename TEvent::TPtr&)>& predicate, - TDuration simTimeout = TDuration::Max()) - { - typename TEvent::TPtr handle; - const ui32 eventType = TEvent::EventType; + const std::function<bool(const typename TEvent::TPtr&)>& predicate, + TDuration simTimeout = TDuration::Max()) + { + typename TEvent::TPtr handle; + const ui32 eventType = TEvent::EventType; WaitForEdgeEvents([&](TTestActorRuntimeBase& runtime, TAutoPtr<IEventHandle>& event) { - Y_UNUSED(runtime); - if (event->GetTypeRewrite() != eventType) - return false; - - typename TEvent::TPtr* typedEvent = reinterpret_cast<typename TEvent::TPtr*>(&event); - if (predicate(*typedEvent)) { - handle = *typedEvent; - return true; - } - - return false; - }, edgeFilter, simTimeout); - - if (simTimeout == TDuration::Max()) - Y_VERIFY(handle); - - return handle; - } - - template<class TEvent> - typename TEvent::TPtr GrabEdgeEventIf( + Y_UNUSED(runtime); + if (event->GetTypeRewrite() != eventType) + return false; + + typename TEvent::TPtr* typedEvent = reinterpret_cast<typename TEvent::TPtr*>(&event); + if (predicate(*typedEvent)) { + handle = *typedEvent; + return true; + } + + return false; + }, edgeFilter, simTimeout); + + if (simTimeout == TDuration::Max()) + Y_VERIFY(handle); + + return handle; + } + + template<class TEvent> + typename TEvent::TPtr GrabEdgeEventIf( const TActorId& edgeActor, - const std::function<bool(const typename TEvent::TPtr&)>& predicate, - TDuration simTimeout = TDuration::Max()) - { + const std::function<bool(const typename TEvent::TPtr&)>& predicate, + TDuration simTimeout = TDuration::Max()) + { TSet<TActorId> edgeFilter{edgeActor}; - return GrabEdgeEventIf<TEvent>(edgeFilter, predicate, simTimeout); - } - + return GrabEdgeEventIf<TEvent>(edgeFilter, predicate, simTimeout); + } + template <typename TEvent> TEvent* GrabEdgeEvent(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { std::function<bool(const TEvent&)> truth = [](const TEvent&) { return true; }; @@ -367,17 +367,17 @@ namespace NActors { return THolder(handle ? handle->Release<TEvent>().Release() : nullptr); } - template<class TEvent> + template<class TEvent> typename TEvent::TPtr GrabEdgeEvent(const TSet<TActorId>& edgeFilter, TDuration simTimeout = TDuration::Max()) { - return GrabEdgeEventIf<TEvent>(edgeFilter, [](const typename TEvent::TPtr&) { return true; }, simTimeout); - } - - template<class TEvent> + return GrabEdgeEventIf<TEvent>(edgeFilter, [](const typename TEvent::TPtr&) { return true; }, simTimeout); + } + + template<class TEvent> typename TEvent::TPtr GrabEdgeEvent(const TActorId& edgeActor, TDuration simTimeout = TDuration::Max()) { TSet<TActorId> edgeFilter{edgeActor}; - return GrabEdgeEvent<TEvent>(edgeFilter, simTimeout); - } - + return GrabEdgeEvent<TEvent>(edgeFilter, simTimeout); + } + // replace with std::variant<> template <typename... TEvents> std::tuple<TEvents*...> GrabEdgeEvents(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { @@ -388,7 +388,7 @@ namespace NActors { return false; handle = event; return true; - }, {}, simTimeout); + }, {}, simTimeout); if (simTimeout == TDuration::Max()) Y_VERIFY(handle); if (handle) { @@ -408,24 +408,24 @@ namespace NActors { } } - template<class TEvent> + template<class TEvent> typename TEvent::TPtr GrabEdgeEventRethrow(const TSet<TActorId>& edgeFilter, TDuration simTimeout = TDuration::Max()) { - try { - return GrabEdgeEvent<TEvent>(edgeFilter, simTimeout); - } catch (...) { - ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeName<TEvent>() << ": " << CurrentExceptionMessage(); - } - } - - template<class TEvent> + try { + return GrabEdgeEvent<TEvent>(edgeFilter, simTimeout); + } catch (...) { + ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeName<TEvent>() << ": " << CurrentExceptionMessage(); + } + } + + template<class TEvent> typename TEvent::TPtr GrabEdgeEventRethrow(const TActorId& edgeActor, TDuration simTimeout = TDuration::Max()) { - try { - return GrabEdgeEvent<TEvent>(edgeActor, simTimeout); - } catch (...) { - ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeName<TEvent>() << ": " << CurrentExceptionMessage(); - } - } - + try { + return GrabEdgeEvent<TEvent>(edgeActor, simTimeout); + } catch (...) { + ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeName<TEvent>() << ": " << CurrentExceptionMessage(); + } + } + template <typename... TEvents> static TString TypeNames() { static TString names[] = { TypeName<TEvents>()... }; @@ -530,7 +530,7 @@ namespace NActors { ui64 DispatcherRandomSeed; TIntrusivePtr<IRandomProvider> DispatcherRandomProvider; TAutoPtr<TLogBackend> LogBackend; - bool NeedMonitoring; + bool NeedMonitoring; TIntrusivePtr<IRandomProvider> RandomProvider; TIntrusivePtr<ITimeProvider> TimeProvider; @@ -558,7 +558,7 @@ namespace NActors { TIntrusivePtr<NActors::NLog::TSettings> LogSettings; TIntrusivePtr<NInterconnect::TPollerThreads> Poller; volatile ui64* ActorSystemTimestamp; - volatile ui64* ActorSystemMonotonic; + volatile ui64* ActorSystemMonotonic; TVector<std::pair<TActorId, TActorSetupCmd> > LocalServices; TMap<TActorId, IActor*> LocalServicesActors; TMap<IActor*, TActorId> ActorToActorId; @@ -607,7 +607,7 @@ namespace NActors { TMap<const TDispatchOptions::TFinalEventCondition*, ui32> FinalEventFrequency; TSet<TEventMailboxId> FoundNonEmptyMailboxes; - bool FinalEventFound = false; + bool FinalEventFound = false; }; TProgramShouldContinue ShouldContinue; diff --git a/library/cpp/actors/util/unordered_cache.h b/library/cpp/actors/util/unordered_cache.h index 76f036c0cf..6b1b4f838a 100644 --- a/library/cpp/actors/util/unordered_cache.h +++ b/library/cpp/actors/util/unordered_cache.h @@ -3,126 +3,126 @@ #include "defs.h" #include "queue_chunk.h" -template <typename T, ui32 Size = 512, ui32 ConcurrencyFactor = 1, typename TChunk = TQueueChunk<T, Size>> +template <typename T, ui32 Size = 512, ui32 ConcurrencyFactor = 1, typename TChunk = TQueueChunk<T, Size>> class TUnorderedCache : TNonCopyable { static_assert(std::is_integral<T>::value || std::is_pointer<T>::value, "expect std::is_integral<T>::value || std::is_pointer<T>::value"); public: - static constexpr ui32 Concurrency = ConcurrencyFactor * 4; + static constexpr ui32 Concurrency = ConcurrencyFactor * 4; private: - struct TReadSlot { - TChunk* volatile ReadFrom; - volatile ui32 ReadPosition; - char Padding[64 - sizeof(TChunk*) - sizeof(ui32)]; // 1 slot per cache line - }; - - struct TWriteSlot { - TChunk* volatile WriteTo; - volatile ui32 WritePosition; - char Padding[64 - sizeof(TChunk*) - sizeof(ui32)]; // 1 slot per cache line - }; - - static_assert(sizeof(TReadSlot) == 64, "expect sizeof(TReadSlot) == 64"); - static_assert(sizeof(TWriteSlot) == 64, "expect sizeof(TWriteSlot) == 64"); - -private: - TReadSlot ReadSlots[Concurrency]; - TWriteSlot WriteSlots[Concurrency]; - + struct TReadSlot { + TChunk* volatile ReadFrom; + volatile ui32 ReadPosition; + char Padding[64 - sizeof(TChunk*) - sizeof(ui32)]; // 1 slot per cache line + }; + + struct TWriteSlot { + TChunk* volatile WriteTo; + volatile ui32 WritePosition; + char Padding[64 - sizeof(TChunk*) - sizeof(ui32)]; // 1 slot per cache line + }; + + static_assert(sizeof(TReadSlot) == 64, "expect sizeof(TReadSlot) == 64"); + static_assert(sizeof(TWriteSlot) == 64, "expect sizeof(TWriteSlot) == 64"); + +private: + TReadSlot ReadSlots[Concurrency]; + TWriteSlot WriteSlots[Concurrency]; + static_assert(sizeof(TChunk*) == sizeof(TAtomic), "expect sizeof(TChunk*) == sizeof(TAtomic)"); -private: - struct TLockedWriter { - TWriteSlot* Slot; - TChunk* WriteTo; - - TLockedWriter() - : Slot(nullptr) - , WriteTo(nullptr) - { } - - TLockedWriter(TWriteSlot* slot, TChunk* writeTo) - : Slot(slot) - , WriteTo(writeTo) - { } - - ~TLockedWriter() noexcept { - Drop(); - } - - void Drop() { - if (Slot) { - AtomicStore(&Slot->WriteTo, WriteTo); - Slot = nullptr; - } - } - - TLockedWriter(const TLockedWriter&) = delete; - TLockedWriter& operator=(const TLockedWriter&) = delete; - - TLockedWriter(TLockedWriter&& rhs) - : Slot(rhs.Slot) - , WriteTo(rhs.WriteTo) - { - rhs.Slot = nullptr; - } - - TLockedWriter& operator=(TLockedWriter&& rhs) { - if (Y_LIKELY(this != &rhs)) { - Drop(); - Slot = rhs.Slot; - WriteTo = rhs.WriteTo; - rhs.Slot = nullptr; - } - return *this; - } - }; - -private: - TLockedWriter LockWriter(ui64 writerRotation) { - ui32 cycle = 0; +private: + struct TLockedWriter { + TWriteSlot* Slot; + TChunk* WriteTo; + + TLockedWriter() + : Slot(nullptr) + , WriteTo(nullptr) + { } + + TLockedWriter(TWriteSlot* slot, TChunk* writeTo) + : Slot(slot) + , WriteTo(writeTo) + { } + + ~TLockedWriter() noexcept { + Drop(); + } + + void Drop() { + if (Slot) { + AtomicStore(&Slot->WriteTo, WriteTo); + Slot = nullptr; + } + } + + TLockedWriter(const TLockedWriter&) = delete; + TLockedWriter& operator=(const TLockedWriter&) = delete; + + TLockedWriter(TLockedWriter&& rhs) + : Slot(rhs.Slot) + , WriteTo(rhs.WriteTo) + { + rhs.Slot = nullptr; + } + + TLockedWriter& operator=(TLockedWriter&& rhs) { + if (Y_LIKELY(this != &rhs)) { + Drop(); + Slot = rhs.Slot; + WriteTo = rhs.WriteTo; + rhs.Slot = nullptr; + } + return *this; + } + }; + +private: + TLockedWriter LockWriter(ui64 writerRotation) { + ui32 cycle = 0; for (;;) { - TWriteSlot* slot = &WriteSlots[writerRotation % Concurrency]; - if (AtomicLoad(&slot->WriteTo) != nullptr) { - if (TChunk* writeTo = AtomicSwap(&slot->WriteTo, nullptr)) { - return TLockedWriter(slot, writeTo); + TWriteSlot* slot = &WriteSlots[writerRotation % Concurrency]; + if (AtomicLoad(&slot->WriteTo) != nullptr) { + if (TChunk* writeTo = AtomicSwap(&slot->WriteTo, nullptr)) { + return TLockedWriter(slot, writeTo); } } - ++writerRotation; - - // Do a spinlock pause after a full cycle - if (++cycle == Concurrency) { - SpinLockPause(); - cycle = 0; - } - } + ++writerRotation; + + // Do a spinlock pause after a full cycle + if (++cycle == Concurrency) { + SpinLockPause(); + cycle = 0; + } + } } - void WriteOne(TLockedWriter& lock, T x) { + void WriteOne(TLockedWriter& lock, T x) { Y_VERIFY_DEBUG(x != 0); - const ui32 pos = AtomicLoad(&lock.Slot->WritePosition); + const ui32 pos = AtomicLoad(&lock.Slot->WritePosition); if (pos != TChunk::EntriesCount) { - AtomicStore(&lock.Slot->WritePosition, pos + 1); - AtomicStore(&lock.WriteTo->Entries[pos], x); + AtomicStore(&lock.Slot->WritePosition, pos + 1); + AtomicStore(&lock.WriteTo->Entries[pos], x); } else { - TChunk* next = new TChunk(); + TChunk* next = new TChunk(); AtomicStore(&next->Entries[0], x); - AtomicStore(&lock.Slot->WritePosition, 1u); - AtomicStore(&lock.WriteTo->Next, next); - lock.WriteTo = next; + AtomicStore(&lock.Slot->WritePosition, 1u); + AtomicStore(&lock.WriteTo->Next, next); + lock.WriteTo = next; } } public: - TUnorderedCache() { - for (ui32 i = 0; i < Concurrency; ++i) { - ReadSlots[i].ReadFrom = new TChunk(); - ReadSlots[i].ReadPosition = 0; + TUnorderedCache() { + for (ui32 i = 0; i < Concurrency; ++i) { + ReadSlots[i].ReadFrom = new TChunk(); + ReadSlots[i].ReadPosition = 0; - WriteSlots[i].WriteTo = ReadSlots[i].ReadFrom; - WriteSlots[i].WritePosition = 0; + WriteSlots[i].WriteTo = ReadSlots[i].ReadFrom; + WriteSlots[i].WritePosition = 0; } } @@ -130,44 +130,44 @@ public: Y_VERIFY(!Pop(0)); for (ui64 i = 0; i < Concurrency; ++i) { - if (ReadSlots[i].ReadFrom) { - delete ReadSlots[i].ReadFrom; - ReadSlots[i].ReadFrom = nullptr; + if (ReadSlots[i].ReadFrom) { + delete ReadSlots[i].ReadFrom; + ReadSlots[i].ReadFrom = nullptr; } - WriteSlots[i].WriteTo = nullptr; + WriteSlots[i].WriteTo = nullptr; } } - T Pop(ui64 readerRotation) noexcept { + T Pop(ui64 readerRotation) noexcept { ui64 readerIndex = readerRotation; const ui64 endIndex = readerIndex + Concurrency; for (; readerIndex != endIndex; ++readerIndex) { - TReadSlot* slot = &ReadSlots[readerIndex % Concurrency]; - if (AtomicLoad(&slot->ReadFrom) != nullptr) { - if (TChunk* readFrom = AtomicSwap(&slot->ReadFrom, nullptr)) { - const ui32 pos = AtomicLoad(&slot->ReadPosition); + TReadSlot* slot = &ReadSlots[readerIndex % Concurrency]; + if (AtomicLoad(&slot->ReadFrom) != nullptr) { + if (TChunk* readFrom = AtomicSwap(&slot->ReadFrom, nullptr)) { + const ui32 pos = AtomicLoad(&slot->ReadPosition); if (pos != TChunk::EntriesCount) { if (T ret = AtomicLoad(&readFrom->Entries[pos])) { - AtomicStore(&slot->ReadPosition, pos + 1); - AtomicStore(&slot->ReadFrom, readFrom); // release lock with same chunk - return ret; // found, return + AtomicStore(&slot->ReadPosition, pos + 1); + AtomicStore(&slot->ReadFrom, readFrom); // release lock with same chunk + return ret; // found, return } else { - AtomicStore(&slot->ReadFrom, readFrom); // release lock with same chunk + AtomicStore(&slot->ReadFrom, readFrom); // release lock with same chunk } } else if (TChunk* next = AtomicLoad(&readFrom->Next)) { if (T ret = AtomicLoad(&next->Entries[0])) { - AtomicStore(&slot->ReadPosition, 1u); - AtomicStore(&slot->ReadFrom, next); // release lock with next chunk + AtomicStore(&slot->ReadPosition, 1u); + AtomicStore(&slot->ReadFrom, next); // release lock with next chunk delete readFrom; return ret; } else { - AtomicStore(&slot->ReadPosition, 0u); - AtomicStore(&slot->ReadFrom, next); // release lock with new chunk + AtomicStore(&slot->ReadPosition, 0u); + AtomicStore(&slot->ReadFrom, next); // release lock with new chunk delete readFrom; } } else { - // nothing in old chunk and no next chunk, just release lock with old chunk - AtomicStore(&slot->ReadFrom, readFrom); + // nothing in old chunk and no next chunk, just release lock with old chunk + AtomicStore(&slot->ReadFrom, readFrom); } } } @@ -177,20 +177,20 @@ public: } void Push(T x, ui64 writerRotation) { - TLockedWriter lock = LockWriter(writerRotation); - WriteOne(lock, x); + TLockedWriter lock = LockWriter(writerRotation); + WriteOne(lock, x); } void PushBulk(T* x, ui32 xcount, ui64 writerRotation) { - for (;;) { - // Fill no more then one queue chunk per round + for (;;) { + // Fill no more then one queue chunk per round const ui32 xround = Min(xcount, (ui32)TChunk::EntriesCount); - { - TLockedWriter lock = LockWriter(writerRotation++); - for (T* end = x + xround; x != end; ++x) - WriteOne(lock, *x); - } + { + TLockedWriter lock = LockWriter(writerRotation++); + for (T* end = x + xround; x != end; ++x) + WriteOne(lock, *x); + } if (xcount <= TChunk::EntriesCount) break; diff --git a/library/cpp/actors/util/unordered_cache_ut.cpp b/library/cpp/actors/util/unordered_cache_ut.cpp index 37865f2f91..8f15f9c3d5 100644 --- a/library/cpp/actors/util/unordered_cache_ut.cpp +++ b/library/cpp/actors/util/unordered_cache_ut.cpp @@ -1,138 +1,138 @@ -#include "unordered_cache.h" - -#include <library/cpp/testing/unittest/registar.h> -#include <util/random/random.h> -#include <util/system/hp_timer.h> -#include <util/system/sanitizers.h> -#include <util/system/thread.h> - -Y_UNIT_TEST_SUITE(UnorderedCache) { - - void DoOnePushOnePop(ui64 count) { - TUnorderedCache<ui64> queue; - - ui64 readRotation = 0; - ui64 writeRotation = 0; - - auto popped = queue.Pop(readRotation++); - UNIT_ASSERT_VALUES_EQUAL(popped, 0u); - - for (ui64 i = 0; i < count; ++i) { - queue.Push(i + 1, writeRotation++); - popped = queue.Pop(readRotation++); - UNIT_ASSERT_VALUES_EQUAL(popped, i + 1); - - popped = queue.Pop(readRotation++); - UNIT_ASSERT_VALUES_EQUAL(popped, 0u); - } - } - - Y_UNIT_TEST(OnePushOnePop) { - DoOnePushOnePop(1); - } - - Y_UNIT_TEST(OnePushOnePop_Repeat1M) { - DoOnePushOnePop(1000000); - } - - /** - * Simplified thread spawning for testing - */ - class TWorkerThread : public ISimpleThread { - private: - std::function<void()> Func; - double Time = 0.0; - - public: - TWorkerThread(std::function<void()> func) - : Func(std::move(func)) - { } - - double GetTime() const { - return Time; - } - - static THolder<TWorkerThread> Spawn(std::function<void()> func) { - THolder<TWorkerThread> thread = MakeHolder<TWorkerThread>(std::move(func)); - thread->Start(); - return thread; - } - - private: - void* ThreadProc() noexcept override { - THPTimer timer; - Func(); - Time = timer.Passed(); - return nullptr; - } - }; - - void DoConcurrentPushPop(size_t threads, ui64 perThreadCount) { - // Concurrency factor 4 is up to 16 threads - TUnorderedCache<ui64, 512, 4> queue; - - auto workerFunc = [&](size_t threadIndex) { - ui64 readRotation = 0; - ui64 writeRotation = 0; - ui64 readsDone = 0; - ui64 writesDone = 0; - for (;;) { - bool canRead = readsDone < writesDone; - bool canWrite = writesDone < perThreadCount; - if (!canRead && !canWrite) { - break; - } - if (canRead && canWrite) { - // Randomly choose between read and write - if (RandomNumber<ui64>(2)) { - canRead = false; - } else { - canWrite = false; - } - } - if (canRead) { - ui64 popped = queue.Pop(readRotation++); - if (popped) { - ++readsDone; - } - } - if (canWrite) { - queue.Push(1 + writesDone * threads + threadIndex, writeRotation++); - ++writesDone; - } - } - }; - - TVector<THolder<TWorkerThread>> workers(threads); - for (size_t i = 0; i < threads; ++i) { - workers[i] = TWorkerThread::Spawn([workerFunc, i]() { - workerFunc(i); - }); - } - - double maxTime = 0; - for (size_t i = 0; i < threads; ++i) { - workers[i]->Join(); - maxTime = Max(maxTime, workers[i]->GetTime()); - } - - auto popped = queue.Pop(0); - UNIT_ASSERT_VALUES_EQUAL(popped, 0u); - - Cerr << "Concurrent with " << threads << " threads: " << maxTime << " seconds" << Endl; - } - - void DoConcurrentPushPop_3times(size_t threads, ui64 perThreadCount) { - for (size_t i = 0; i < 3; ++i) { - DoConcurrentPushPop(threads, perThreadCount); - } - } - - static constexpr ui64 PER_THREAD_COUNT = NSan::PlainOrUnderSanitizer(1000000, 100000); - - Y_UNIT_TEST(ConcurrentPushPop_1thread) { DoConcurrentPushPop_3times(1, PER_THREAD_COUNT); } - Y_UNIT_TEST(ConcurrentPushPop_2threads) { DoConcurrentPushPop_3times(2, PER_THREAD_COUNT); } - Y_UNIT_TEST(ConcurrentPushPop_4threads) { DoConcurrentPushPop_3times(4, PER_THREAD_COUNT); } - Y_UNIT_TEST(ConcurrentPushPop_8threads) { DoConcurrentPushPop_3times(8, PER_THREAD_COUNT); } - Y_UNIT_TEST(ConcurrentPushPop_16threads) { DoConcurrentPushPop_3times(16, PER_THREAD_COUNT); } -} +#include "unordered_cache.h" + +#include <library/cpp/testing/unittest/registar.h> +#include <util/random/random.h> +#include <util/system/hp_timer.h> +#include <util/system/sanitizers.h> +#include <util/system/thread.h> + +Y_UNIT_TEST_SUITE(UnorderedCache) { + + void DoOnePushOnePop(ui64 count) { + TUnorderedCache<ui64> queue; + + ui64 readRotation = 0; + ui64 writeRotation = 0; + + auto popped = queue.Pop(readRotation++); + UNIT_ASSERT_VALUES_EQUAL(popped, 0u); + + for (ui64 i = 0; i < count; ++i) { + queue.Push(i + 1, writeRotation++); + popped = queue.Pop(readRotation++); + UNIT_ASSERT_VALUES_EQUAL(popped, i + 1); + + popped = queue.Pop(readRotation++); + UNIT_ASSERT_VALUES_EQUAL(popped, 0u); + } + } + + Y_UNIT_TEST(OnePushOnePop) { + DoOnePushOnePop(1); + } + + Y_UNIT_TEST(OnePushOnePop_Repeat1M) { + DoOnePushOnePop(1000000); + } + + /** + * Simplified thread spawning for testing + */ + class TWorkerThread : public ISimpleThread { + private: + std::function<void()> Func; + double Time = 0.0; + + public: + TWorkerThread(std::function<void()> func) + : Func(std::move(func)) + { } + + double GetTime() const { + return Time; + } + + static THolder<TWorkerThread> Spawn(std::function<void()> func) { + THolder<TWorkerThread> thread = MakeHolder<TWorkerThread>(std::move(func)); + thread->Start(); + return thread; + } + + private: + void* ThreadProc() noexcept override { + THPTimer timer; + Func(); + Time = timer.Passed(); + return nullptr; + } + }; + + void DoConcurrentPushPop(size_t threads, ui64 perThreadCount) { + // Concurrency factor 4 is up to 16 threads + TUnorderedCache<ui64, 512, 4> queue; + + auto workerFunc = [&](size_t threadIndex) { + ui64 readRotation = 0; + ui64 writeRotation = 0; + ui64 readsDone = 0; + ui64 writesDone = 0; + for (;;) { + bool canRead = readsDone < writesDone; + bool canWrite = writesDone < perThreadCount; + if (!canRead && !canWrite) { + break; + } + if (canRead && canWrite) { + // Randomly choose between read and write + if (RandomNumber<ui64>(2)) { + canRead = false; + } else { + canWrite = false; + } + } + if (canRead) { + ui64 popped = queue.Pop(readRotation++); + if (popped) { + ++readsDone; + } + } + if (canWrite) { + queue.Push(1 + writesDone * threads + threadIndex, writeRotation++); + ++writesDone; + } + } + }; + + TVector<THolder<TWorkerThread>> workers(threads); + for (size_t i = 0; i < threads; ++i) { + workers[i] = TWorkerThread::Spawn([workerFunc, i]() { + workerFunc(i); + }); + } + + double maxTime = 0; + for (size_t i = 0; i < threads; ++i) { + workers[i]->Join(); + maxTime = Max(maxTime, workers[i]->GetTime()); + } + + auto popped = queue.Pop(0); + UNIT_ASSERT_VALUES_EQUAL(popped, 0u); + + Cerr << "Concurrent with " << threads << " threads: " << maxTime << " seconds" << Endl; + } + + void DoConcurrentPushPop_3times(size_t threads, ui64 perThreadCount) { + for (size_t i = 0; i < 3; ++i) { + DoConcurrentPushPop(threads, perThreadCount); + } + } + + static constexpr ui64 PER_THREAD_COUNT = NSan::PlainOrUnderSanitizer(1000000, 100000); + + Y_UNIT_TEST(ConcurrentPushPop_1thread) { DoConcurrentPushPop_3times(1, PER_THREAD_COUNT); } + Y_UNIT_TEST(ConcurrentPushPop_2threads) { DoConcurrentPushPop_3times(2, PER_THREAD_COUNT); } + Y_UNIT_TEST(ConcurrentPushPop_4threads) { DoConcurrentPushPop_3times(4, PER_THREAD_COUNT); } + Y_UNIT_TEST(ConcurrentPushPop_8threads) { DoConcurrentPushPop_3times(8, PER_THREAD_COUNT); } + Y_UNIT_TEST(ConcurrentPushPop_16threads) { DoConcurrentPushPop_3times(16, PER_THREAD_COUNT); } +} diff --git a/library/cpp/actors/util/ut/ya.make b/library/cpp/actors/util/ut/ya.make index 3b08b77984..5546525c3a 100644 --- a/library/cpp/actors/util/ut/ya.make +++ b/library/cpp/actors/util/ut/ya.make @@ -12,7 +12,7 @@ OWNER( SRCS( rope_ut.cpp - unordered_cache_ut.cpp + unordered_cache_ut.cpp ) END() diff --git a/library/cpp/actors/ya.make b/library/cpp/actors/ya.make index 737c7fbc18..7d7eb1fa85 100644 --- a/library/cpp/actors/ya.make +++ b/library/cpp/actors/ya.make @@ -3,7 +3,7 @@ RECURSE_FOR_TESTS(ut) RECURSE( log_backend core - dnsresolver + dnsresolver examples memory_log helpers diff --git a/library/cpp/execprofile/profile.cpp b/library/cpp/execprofile/profile.cpp index d05de20203..be1fc21670 100644 --- a/library/cpp/execprofile/profile.cpp +++ b/library/cpp/execprofile/profile.cpp @@ -188,11 +188,11 @@ private: #endif #elif defined _darwin_ #if defined _64_ -#if defined(_arm_) - return (void*)(*mctx)->__ss.__pc; -#else +#if defined(_arm_) + return (void*)(*mctx)->__ss.__pc; +#else return (void*)(*mctx)->__ss.__rip; -#endif +#endif #else #if defined(__IOS__) return (void*)(*mctx)->__ss.__pc; diff --git a/library/cpp/grpc/client/grpc_client_low.cpp b/library/cpp/grpc/client/grpc_client_low.cpp index 73cc908ef8..bba83aee02 100644 --- a/library/cpp/grpc/client/grpc_client_low.cpp +++ b/library/cpp/grpc/client/grpc_client_low.cpp @@ -193,214 +193,214 @@ static void PullEvents(grpc::CompletionQueue* cq) { } } -class TGRpcClientLow::TContextImpl final - : public std::enable_shared_from_this<TContextImpl> - , public IQueueClientContext -{ - friend class TGRpcClientLow; - - using TCallback = std::function<void()>; - using TContextPtr = std::shared_ptr<TContextImpl>; - -public: +class TGRpcClientLow::TContextImpl final + : public std::enable_shared_from_this<TContextImpl> + , public IQueueClientContext +{ + friend class TGRpcClientLow; + + using TCallback = std::function<void()>; + using TContextPtr = std::shared_ptr<TContextImpl>; + +public: ~TContextImpl() override { - Y_VERIFY(CountChildren() == 0, - "Destructor called with non-empty children"); - - if (Parent) { - Parent->ForgetContext(this); - } else if (Y_LIKELY(Owner)) { - Owner->ForgetContext(this); - } - } - - /** - * Helper for locking child pointer from a parent container - */ - static TContextPtr LockChildPtr(TContextImpl* ptr) { - if (ptr) { - // N.B. it is safe to do as long as it's done under a mutex and - // pointer is among valid children. When that's the case we - // know that TContextImpl destructor has not finished yet, so - // the object is valid. The lock() method may return nullptr - // though, if the object is being destructed right now. - return ptr->weak_from_this().lock(); - } else { - return nullptr; - } - } - - void ForgetContext(TContextImpl* child) { - std::unique_lock<std::mutex> guard(Mutex); - - auto removed = RemoveChild(child); - Y_VERIFY(removed, "Unexpected ForgetContext(%p)", child); - } - - IQueueClientContextPtr CreateContext() override { - auto self = shared_from_this(); - auto child = std::make_shared<TContextImpl>(); - - { - std::unique_lock<std::mutex> guard(Mutex); - - AddChild(child.get()); - - // It's now safe to initialize parent and owner - child->Parent = std::move(self); - child->Owner = Owner; + Y_VERIFY(CountChildren() == 0, + "Destructor called with non-empty children"); + + if (Parent) { + Parent->ForgetContext(this); + } else if (Y_LIKELY(Owner)) { + Owner->ForgetContext(this); + } + } + + /** + * Helper for locking child pointer from a parent container + */ + static TContextPtr LockChildPtr(TContextImpl* ptr) { + if (ptr) { + // N.B. it is safe to do as long as it's done under a mutex and + // pointer is among valid children. When that's the case we + // know that TContextImpl destructor has not finished yet, so + // the object is valid. The lock() method may return nullptr + // though, if the object is being destructed right now. + return ptr->weak_from_this().lock(); + } else { + return nullptr; + } + } + + void ForgetContext(TContextImpl* child) { + std::unique_lock<std::mutex> guard(Mutex); + + auto removed = RemoveChild(child); + Y_VERIFY(removed, "Unexpected ForgetContext(%p)", child); + } + + IQueueClientContextPtr CreateContext() override { + auto self = shared_from_this(); + auto child = std::make_shared<TContextImpl>(); + + { + std::unique_lock<std::mutex> guard(Mutex); + + AddChild(child.get()); + + // It's now safe to initialize parent and owner + child->Parent = std::move(self); + child->Owner = Owner; child->CQ = CQ; - - // Propagate cancellation to a child context - if (Cancelled.load(std::memory_order_relaxed)) { - child->Cancelled.store(true, std::memory_order_relaxed); - } - } - - return child; - } - - grpc::CompletionQueue* CompletionQueue() override { - Y_VERIFY(Owner, "Uninitialized context"); + + // Propagate cancellation to a child context + if (Cancelled.load(std::memory_order_relaxed)) { + child->Cancelled.store(true, std::memory_order_relaxed); + } + } + + return child; + } + + grpc::CompletionQueue* CompletionQueue() override { + Y_VERIFY(Owner, "Uninitialized context"); return CQ; - } - - bool IsCancelled() const override { - return Cancelled.load(std::memory_order_acquire); - } - - bool Cancel() override { - TStackVec<TCallback, 1> callbacks; - TStackVec<TContextPtr, 2> children; - - { - std::unique_lock<std::mutex> guard(Mutex); - - if (Cancelled.load(std::memory_order_relaxed)) { - // Already cancelled in another thread - return false; - } - - callbacks.reserve(Callbacks.size()); - children.reserve(CountChildren()); - - for (auto& callback : Callbacks) { - callbacks.emplace_back().swap(callback); - } - Callbacks.clear(); - - // Collect all children we need to cancel - // N.B. we don't clear children links (cleared by destructors) - // N.B. some children may be stuck in destructors at the moment - for (TContextImpl* ptr : InlineChildren) { - if (auto child = LockChildPtr(ptr)) { - children.emplace_back(std::move(child)); - } - } - for (auto* ptr : Children) { - if (auto child = LockChildPtr(ptr)) { - children.emplace_back(std::move(child)); - } - } - - Cancelled.store(true, std::memory_order_release); - } - - // Call directly subscribed callbacks - if (callbacks) { - RunCallbacksNoExcept(callbacks); - } - - // Cancel all children - for (auto& child : children) { - child->Cancel(); - child.reset(); - } - - return true; - } - - void SubscribeCancel(TCallback callback) override { - Y_VERIFY(callback, "SubscribeCancel called with an empty callback"); - - { - std::unique_lock<std::mutex> guard(Mutex); - - if (!Cancelled.load(std::memory_order_relaxed)) { - Callbacks.emplace_back().swap(callback); - return; - } - } - - // Already cancelled, run immediately - callback(); - } - -private: - void AddChild(TContextImpl* child) { - for (TContextImpl*& slot : InlineChildren) { - if (!slot) { - slot = child; - return; - } - } - - Children.insert(child); - } - - bool RemoveChild(TContextImpl* child) { - for (TContextImpl*& slot : InlineChildren) { - if (slot == child) { - slot = nullptr; - return true; - } - } - - return Children.erase(child); - } - - size_t CountChildren() { - size_t count = 0; - - for (TContextImpl* ptr : InlineChildren) { - if (ptr) { - ++count; - } - } - - return count + Children.size(); - } - - template<class TCallbacks> - static void RunCallbacksNoExcept(TCallbacks& callbacks) noexcept { - for (auto& callback : callbacks) { - if (callback) { - callback(); - callback = nullptr; - } - } - } - -private: - // We want a simple lock here, without extra memory allocations - std::mutex Mutex; - - // These fields are initialized on successful registration - TContextPtr Parent; - TGRpcClientLow* Owner = nullptr; + } + + bool IsCancelled() const override { + return Cancelled.load(std::memory_order_acquire); + } + + bool Cancel() override { + TStackVec<TCallback, 1> callbacks; + TStackVec<TContextPtr, 2> children; + + { + std::unique_lock<std::mutex> guard(Mutex); + + if (Cancelled.load(std::memory_order_relaxed)) { + // Already cancelled in another thread + return false; + } + + callbacks.reserve(Callbacks.size()); + children.reserve(CountChildren()); + + for (auto& callback : Callbacks) { + callbacks.emplace_back().swap(callback); + } + Callbacks.clear(); + + // Collect all children we need to cancel + // N.B. we don't clear children links (cleared by destructors) + // N.B. some children may be stuck in destructors at the moment + for (TContextImpl* ptr : InlineChildren) { + if (auto child = LockChildPtr(ptr)) { + children.emplace_back(std::move(child)); + } + } + for (auto* ptr : Children) { + if (auto child = LockChildPtr(ptr)) { + children.emplace_back(std::move(child)); + } + } + + Cancelled.store(true, std::memory_order_release); + } + + // Call directly subscribed callbacks + if (callbacks) { + RunCallbacksNoExcept(callbacks); + } + + // Cancel all children + for (auto& child : children) { + child->Cancel(); + child.reset(); + } + + return true; + } + + void SubscribeCancel(TCallback callback) override { + Y_VERIFY(callback, "SubscribeCancel called with an empty callback"); + + { + std::unique_lock<std::mutex> guard(Mutex); + + if (!Cancelled.load(std::memory_order_relaxed)) { + Callbacks.emplace_back().swap(callback); + return; + } + } + + // Already cancelled, run immediately + callback(); + } + +private: + void AddChild(TContextImpl* child) { + for (TContextImpl*& slot : InlineChildren) { + if (!slot) { + slot = child; + return; + } + } + + Children.insert(child); + } + + bool RemoveChild(TContextImpl* child) { + for (TContextImpl*& slot : InlineChildren) { + if (slot == child) { + slot = nullptr; + return true; + } + } + + return Children.erase(child); + } + + size_t CountChildren() { + size_t count = 0; + + for (TContextImpl* ptr : InlineChildren) { + if (ptr) { + ++count; + } + } + + return count + Children.size(); + } + + template<class TCallbacks> + static void RunCallbacksNoExcept(TCallbacks& callbacks) noexcept { + for (auto& callback : callbacks) { + if (callback) { + callback(); + callback = nullptr; + } + } + } + +private: + // We want a simple lock here, without extra memory allocations + std::mutex Mutex; + + // These fields are initialized on successful registration + TContextPtr Parent; + TGRpcClientLow* Owner = nullptr; grpc::CompletionQueue* CQ = nullptr; - - // Some children are stored inline, others are in a set - std::array<TContextImpl*, 2> InlineChildren{ { nullptr, nullptr } }; + + // Some children are stored inline, others are in a set + std::array<TContextImpl*, 2> InlineChildren{ { nullptr, nullptr } }; std::unordered_set<TContextImpl*> Children; - - // Single callback is stored without extra allocations - TStackVec<TCallback, 1> Callbacks; - - // Atomic flag for a faster IsCancelled() implementation - std::atomic<bool> Cancelled; -}; - + + // Single callback is stored without extra allocations + TStackVec<TCallback, 1> Callbacks; + + // Atomic flag for a faster IsCancelled() implementation + std::atomic<bool> Cancelled; +}; + TGRpcClientLow::TGRpcClientLow(size_t numWorkerThread, bool useCompletionQueuePerThread) : UseCompletionQueuePerThread_(useCompletionQueuePerThread) { @@ -408,7 +408,7 @@ TGRpcClientLow::TGRpcClientLow(size_t numWorkerThread, bool useCompletionQueuePe } void TGRpcClientLow::Init(size_t numWorkerThread) { - SetCqState(WORKING); + SetCqState(WORKING); if (UseCompletionQueuePerThread_) { for (size_t i = 0; i < numWorkerThread; i++) { CQS_.push_back(std::make_unique<grpc::CompletionQueue>()); @@ -428,7 +428,7 @@ void TGRpcClientLow::Init(size_t numWorkerThread) { } } -void TGRpcClientLow::AddWorkerThreadForTest() { +void TGRpcClientLow::AddWorkerThreadForTest() { if (UseCompletionQueuePerThread_) { CQS_.push_back(std::make_unique<grpc::CompletionQueue>()); auto* cq = CQS_.back().get(); @@ -441,75 +441,75 @@ void TGRpcClientLow::AddWorkerThreadForTest() { PullEvents(cq); }).Release()); } -} - -TGRpcClientLow::~TGRpcClientLow() { - StopInternal(true); - WaitInternal(); -} - -void TGRpcClientLow::Stop(bool wait) { - StopInternal(false); - - if (wait) { - WaitInternal(); +} + +TGRpcClientLow::~TGRpcClientLow() { + StopInternal(true); + WaitInternal(); +} + +void TGRpcClientLow::Stop(bool wait) { + StopInternal(false); + + if (wait) { + WaitInternal(); } } -void TGRpcClientLow::StopInternal(bool silent) { - bool shutdown; - - TVector<TContextImpl::TContextPtr> cancelQueue; - +void TGRpcClientLow::StopInternal(bool silent) { + bool shutdown; + + TVector<TContextImpl::TContextPtr> cancelQueue; + { std::unique_lock<std::mutex> guard(Mtx_); - auto allowStateChange = [&]() { - switch (GetCqState()) { - case WORKING: - return true; - case STOP_SILENT: - return !silent; - case STOP_EXPLICIT: - return false; - } - - Y_UNREACHABLE(); - }; - - if (!allowStateChange()) { - // Completion queue is already stopping - return; - } - - SetCqState(silent ? STOP_SILENT : STOP_EXPLICIT); - + auto allowStateChange = [&]() { + switch (GetCqState()) { + case WORKING: + return true; + case STOP_SILENT: + return !silent; + case STOP_EXPLICIT: + return false; + } + + Y_UNREACHABLE(); + }; + + if (!allowStateChange()) { + // Completion queue is already stopping + return; + } + + SetCqState(silent ? STOP_SILENT : STOP_EXPLICIT); + if (!silent && !Contexts_.empty()) { - cancelQueue.reserve(Contexts_.size()); - for (auto* ptr : Contexts_) { - // N.B. some contexts may be stuck in destructors - if (auto context = TContextImpl::LockChildPtr(ptr)) { - cancelQueue.emplace_back(std::move(context)); - } - } - } - + cancelQueue.reserve(Contexts_.size()); + for (auto* ptr : Contexts_) { + // N.B. some contexts may be stuck in destructors + if (auto context = TContextImpl::LockChildPtr(ptr)) { + cancelQueue.emplace_back(std::move(context)); + } + } + } + shutdown = Contexts_.empty(); - } - - for (auto& context : cancelQueue) { - context->Cancel(); - context.reset(); - } - - if (shutdown) { + } + + for (auto& context : cancelQueue) { + context->Cancel(); + context.reset(); + } + + if (shutdown) { for (auto& cq : CQS_) { cq->Shutdown(); } } -} - -void TGRpcClientLow::WaitInternal() { +} + +void TGRpcClientLow::WaitInternal() { std::unique_lock<std::mutex> guard(JoinMutex_); for (auto& ti : WorkerThreads_) { @@ -517,7 +517,7 @@ void TGRpcClientLow::WaitInternal() { } } -void TGRpcClientLow::WaitIdle() { +void TGRpcClientLow::WaitIdle() { std::unique_lock<std::mutex> guard(Mtx_); while (!Contexts_.empty()) { @@ -525,7 +525,7 @@ void TGRpcClientLow::WaitIdle() { } } -std::shared_ptr<IQueueClientContext> TGRpcClientLow::CreateContext() { +std::shared_ptr<IQueueClientContext> TGRpcClientLow::CreateContext() { std::unique_lock<std::mutex> guard(Mtx_); auto allowCreateContext = [&]() { @@ -535,15 +535,15 @@ std::shared_ptr<IQueueClientContext> TGRpcClientLow::CreateContext() { case STOP_SILENT: case STOP_EXPLICIT: return false; - } - + } + Y_UNREACHABLE(); }; if (!allowCreateContext()) { // New context creation is forbidden return nullptr; - } + } auto context = std::make_shared<TContextImpl>(); Contexts_.insert(context.get()); @@ -554,32 +554,32 @@ std::shared_ptr<IQueueClientContext> TGRpcClientLow::CreateContext() { context->CQ = CQS_[0].get(); } return context; -} - -void TGRpcClientLow::ForgetContext(TContextImpl* context) { - bool shutdown = false; - +} + +void TGRpcClientLow::ForgetContext(TContextImpl* context) { + bool shutdown = false; + { std::unique_lock<std::mutex> guard(Mtx_); - if (!Contexts_.erase(context)) { - Y_FAIL("Unexpected ForgetContext(%p)", context); - } - - if (Contexts_.empty()) { - if (IsStopping()) { - shutdown = true; - } - + if (!Contexts_.erase(context)) { + Y_FAIL("Unexpected ForgetContext(%p)", context); + } + + if (Contexts_.empty()) { + if (IsStopping()) { + shutdown = true; + } + ContextsEmpty_.notify_all(); - } - } - - if (shutdown) { - // This was the last context, shutdown CQ + } + } + + if (shutdown) { + // This was the last context, shutdown CQ for (auto& cq : CQS_) { cq->Shutdown(); - } + } } } diff --git a/library/cpp/grpc/client/grpc_client_low.h b/library/cpp/grpc/client/grpc_client_low.h index ab0a0627be..5d0983f804 100644 --- a/library/cpp/grpc/client/grpc_client_low.h +++ b/library/cpp/grpc/client/grpc_client_low.h @@ -52,125 +52,125 @@ public: virtual void Destroy() = 0; }; -// Implementation of IQueueClientEvent that reduces allocations -template<class TSelf> -class TQueueClientFixedEvent : private IQueueClientEvent { - using TCallback = void (TSelf::*)(bool); - -public: - TQueueClientFixedEvent(TSelf* self, TCallback callback) - : Self(self) - , Callback(callback) - { } - - IQueueClientEvent* Prepare() { - Self->Ref(); - return this; - } - -private: - bool Execute(bool ok) override { - ((*Self).*Callback)(ok); - return false; - } - - void Destroy() override { - Self->UnRef(); - } - -private: - TSelf* const Self; - TCallback const Callback; -}; - -class IQueueClientContext; -using IQueueClientContextPtr = std::shared_ptr<IQueueClientContext>; - -// Provider of IQueueClientContext instances -class IQueueClientContextProvider { -public: - virtual ~IQueueClientContextProvider() = default; - - virtual IQueueClientContextPtr CreateContext() = 0; -}; - -// Activity context for a low-level client -class IQueueClientContext : public IQueueClientContextProvider { -public: - virtual ~IQueueClientContext() = default; - - //! Returns CompletionQueue associated with the client - virtual grpc::CompletionQueue* CompletionQueue() = 0; - - //! Returns true if context has been cancelled - virtual bool IsCancelled() const = 0; - - //! Tries to cancel context, calling all registered callbacks - virtual bool Cancel() = 0; - - //! Subscribes callback to cancellation - // - // Note there's no way to unsubscribe, if subscription is temporary - // make sure you create a new context with CreateContext and release - // it as soon as it's no longer needed. - virtual void SubscribeCancel(std::function<void()> callback) = 0; - - //! Subscribes callback to cancellation - // - // This alias is for compatibility with older code. - void SubscribeStop(std::function<void()> callback) { - SubscribeCancel(std::move(callback)); - } -}; - +// Implementation of IQueueClientEvent that reduces allocations +template<class TSelf> +class TQueueClientFixedEvent : private IQueueClientEvent { + using TCallback = void (TSelf::*)(bool); + +public: + TQueueClientFixedEvent(TSelf* self, TCallback callback) + : Self(self) + , Callback(callback) + { } + + IQueueClientEvent* Prepare() { + Self->Ref(); + return this; + } + +private: + bool Execute(bool ok) override { + ((*Self).*Callback)(ok); + return false; + } + + void Destroy() override { + Self->UnRef(); + } + +private: + TSelf* const Self; + TCallback const Callback; +}; + +class IQueueClientContext; +using IQueueClientContextPtr = std::shared_ptr<IQueueClientContext>; + +// Provider of IQueueClientContext instances +class IQueueClientContextProvider { +public: + virtual ~IQueueClientContextProvider() = default; + + virtual IQueueClientContextPtr CreateContext() = 0; +}; + +// Activity context for a low-level client +class IQueueClientContext : public IQueueClientContextProvider { +public: + virtual ~IQueueClientContext() = default; + + //! Returns CompletionQueue associated with the client + virtual grpc::CompletionQueue* CompletionQueue() = 0; + + //! Returns true if context has been cancelled + virtual bool IsCancelled() const = 0; + + //! Tries to cancel context, calling all registered callbacks + virtual bool Cancel() = 0; + + //! Subscribes callback to cancellation + // + // Note there's no way to unsubscribe, if subscription is temporary + // make sure you create a new context with CreateContext and release + // it as soon as it's no longer needed. + virtual void SubscribeCancel(std::function<void()> callback) = 0; + + //! Subscribes callback to cancellation + // + // This alias is for compatibility with older code. + void SubscribeStop(std::function<void()> callback) { + SubscribeCancel(std::move(callback)); + } +}; + // Represents grpc status and error message string struct TGrpcStatus { - TString Msg; + TString Msg; TString Details; - int GRpcStatusCode; - bool InternalError; - - TGrpcStatus() - : GRpcStatusCode(grpc::StatusCode::OK) - , InternalError(false) - { } - - TGrpcStatus(TString msg, int statusCode, bool internalError) - : Msg(std::move(msg)) - , GRpcStatusCode(statusCode) - , InternalError(internalError) - { } - + int GRpcStatusCode; + bool InternalError; + + TGrpcStatus() + : GRpcStatusCode(grpc::StatusCode::OK) + , InternalError(false) + { } + + TGrpcStatus(TString msg, int statusCode, bool internalError) + : Msg(std::move(msg)) + , GRpcStatusCode(statusCode) + , InternalError(internalError) + { } + TGrpcStatus(grpc::StatusCode status, TString msg, TString details = {}) - : Msg(std::move(msg)) + : Msg(std::move(msg)) , Details(std::move(details)) - , GRpcStatusCode(status) - , InternalError(false) - { } - - TGrpcStatus(const grpc::Status& status) + , GRpcStatusCode(status) + , InternalError(false) + { } + + TGrpcStatus(const grpc::Status& status) : TGrpcStatus(status.error_code(), TString(status.error_message()), TString(status.error_details())) - { } - - TGrpcStatus& operator=(const grpc::Status& status) { - Msg = TString(status.error_message()); + { } + + TGrpcStatus& operator=(const grpc::Status& status) { + Msg = TString(status.error_message()); Details = TString(status.error_details()); - GRpcStatusCode = status.error_code(); - InternalError = false; - return *this; - } - - static TGrpcStatus Internal(TString msg) { - return { std::move(msg), -1, true }; - } - - bool Ok() const { - return !InternalError && GRpcStatusCode == grpc::StatusCode::OK; - } + GRpcStatusCode = status.error_code(); + InternalError = false; + return *this; + } + + static TGrpcStatus Internal(TString msg) { + return { std::move(msg), -1, true }; + } + + bool Ok() const { + return !InternalError && GRpcStatusCode == grpc::StatusCode::OK; + } }; bool inline IsGRpcStatusGood(const TGrpcStatus& status) { - return status.Ok(); + return status.Ok(); } // Response callback type - this callback will be called when request is finished @@ -241,9 +241,9 @@ public: { } ~TSimpleRequestProcessor() { - if (!Replied_ && Callback_) { - Callback_(TGrpcStatus::Internal("request left unhandled"), std::move(Reply_)); - Callback_ = nullptr; // free resources as early as possible + if (!Replied_ && Callback_) { + Callback_(TGrpcStatus::Internal("request left unhandled"), std::move(Reply_)); + Callback_ = nullptr; // free resources as early as possible } } @@ -251,53 +251,53 @@ public: { std::unique_lock<std::mutex> guard(Mutex_); LocalContext.reset(); - } - TGrpcStatus status; - if (ok) { + } + TGrpcStatus status; + if (ok) { status = Status; - } else { - status = TGrpcStatus::Internal("Unexpected error"); + } else { + status = TGrpcStatus::Internal("Unexpected error"); } Replied_ = true; Callback_(std::move(status), std::move(Reply_)); - Callback_ = nullptr; // free resources as early as possible + Callback_ = nullptr; // free resources as early as possible return false; } void Destroy() override { - UnRef(); + UnRef(); } private: - IQueueClientEvent* FinishedEvent() { - Ref(); - return this; + IQueueClientEvent* FinishedEvent() { + Ref(); + return this; } void Start(TStub& stub, TAsyncRequest asyncRequest, const TRequest& request, IQueueClientContextProvider* provider) { - auto context = provider->CreateContext(); - if (!context) { - Replied_ = true; - Callback_(TGrpcStatus(grpc::StatusCode::CANCELLED, "Client is shutting down"), std::move(Reply_)); - Callback_ = nullptr; - return; - } + auto context = provider->CreateContext(); + if (!context) { + Replied_ = true; + Callback_(TGrpcStatus(grpc::StatusCode::CANCELLED, "Client is shutting down"), std::move(Reply_)); + Callback_ = nullptr; + return; + } { std::unique_lock<std::mutex> guard(Mutex_); LocalContext = context; Reader_ = (stub.*asyncRequest)(&Context, request, context->CompletionQueue()); Reader_->Finish(&Reply_, &Status, FinishedEvent()); - } - context->SubscribeStop([self = TPtr(this)] { - self->Stop(); - }); - } - - void Stop() { + } + context->SubscribeStop([self = TPtr(this)] { + self->Stop(); + }); + } + + void Stop() { Context.TryCancel(); - } - - TResponseCallback<TResponse> Callback_; + } + + TResponseCallback<TResponse> Callback_; TResponse Reply_; std::mutex Mutex_; TAsyncReaderPtr Reader_; @@ -387,49 +387,49 @@ private: template<class TResponse> class IStreamRequestReadProcessor : public TThrRefBase { -public: +public: using TPtr = TIntrusivePtr<IStreamRequestReadProcessor>; - using TReadCallback = std::function<void(TGrpcStatus&&)>; - - /** - * Asynchronously cancel the request - */ - virtual void Cancel() = 0; - - /** + using TReadCallback = std::function<void(TGrpcStatus&&)>; + + /** + * Asynchronously cancel the request + */ + virtual void Cancel() = 0; + + /** * Scheduled initial server metadata read from the stream */ virtual void ReadInitialMetadata(std::unordered_multimap<TString, TString>* metadata, TReadCallback callback) = 0; /** - * Scheduled response read from the stream - * Callback will be called with the status if it failed - * Only one Read or Finish call may be active at a time - */ - virtual void Read(TResponse* response, TReadCallback callback) = 0; - - /** - * Stop reading and gracefully finish the stream - * Only one Read or Finish call may be active at a time - */ - virtual void Finish(TReadCallback callback) = 0; - - /** - * Additional callback to be called when stream has finished - */ - virtual void AddFinishedCallback(TReadCallback callback) = 0; -}; - + * Scheduled response read from the stream + * Callback will be called with the status if it failed + * Only one Read or Finish call may be active at a time + */ + virtual void Read(TResponse* response, TReadCallback callback) = 0; + + /** + * Stop reading and gracefully finish the stream + * Only one Read or Finish call may be active at a time + */ + virtual void Finish(TReadCallback callback) = 0; + + /** + * Additional callback to be called when stream has finished + */ + virtual void AddFinishedCallback(TReadCallback callback) = 0; +}; + template<class TRequest, class TResponse> class IStreamRequestReadWriteProcessor : public IStreamRequestReadProcessor<TResponse> { public: using TPtr = TIntrusivePtr<IStreamRequestReadWriteProcessor>; - using TWriteCallback = std::function<void(TGrpcStatus&&)>; + using TWriteCallback = std::function<void(TGrpcStatus&&)>; /** * Scheduled request write to the stream */ - virtual void Write(TRequest&& request, TWriteCallback callback = { }) = 0; + virtual void Write(TRequest&& request, TWriteCallback callback = { }) = 0; }; class TGRpcKeepAliveSocketMutator; @@ -548,7 +548,7 @@ public: { std::unique_lock<std::mutex> guard(Mutex); Cancelled = true; - if (Started && !ReadFinished) { + if (Started && !ReadFinished) { if (!ReadActive) { ReadFinished = true; } @@ -640,31 +640,31 @@ public: callback(std::move(status)); } - - void AddFinishedCallback(TReadCallback callback) override { - Y_VERIFY(callback, "Unexpected empty callback"); - - TGrpcStatus status; - + + void AddFinishedCallback(TReadCallback callback) override { + Y_VERIFY(callback, "Unexpected empty callback"); + + TGrpcStatus status; + { std::unique_lock<std::mutex> guard(Mutex); - if (!Finished) { - FinishedCallbacks.emplace_back().swap(callback); - return; - } - - if (FinishedOk) { - status = Status; - } else if (Cancelled) { - status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); - } else { - status = TGrpcStatus::Internal("Unexpected error"); - } - } - - callback(std::move(status)); - } - + if (!Finished) { + FinishedCallbacks.emplace_back().swap(callback); + return; + } + + if (FinishedOk) { + status = Status; + } else if (Cancelled) { + status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); + } else { + status = TGrpcStatus::Internal("Unexpected error"); + } + } + + callback(std::move(status)); + } + private: void Start(TStub& stub, const TRequest& request, TAsyncRequest asyncRequest, IQueueClientContextProvider* provider) { auto context = provider->CreateContext(); @@ -727,8 +727,8 @@ private: { std::unique_lock<std::mutex> guard(Mutex); - Started = true; - if (!ok || Cancelled) { + Started = true; + if (!ok || Cancelled) { ReadFinished = true; Stream->Finish(&Status, OnFinishedTag.Prepare()); return; @@ -743,7 +743,7 @@ private: void OnFinished(bool ok) { TGrpcStatus status; std::vector<TReadCallback> finishedCallbacks; - TReaderCallback startCallback; + TReaderCallback startCallback; TReadCallback readCallback; TReadCallback finishCallback; @@ -756,19 +756,19 @@ private: if (ok) { status = Status; - } else if (Cancelled) { - status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); + } else if (Cancelled) { + status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); } else { status = TGrpcStatus::Internal("Unexpected error"); } - finishedCallbacks.swap(FinishedCallbacks); - - if (Callback) { - Y_VERIFY(!ReadActive); - startCallback = std::move(Callback); - Callback = nullptr; - } else if (ReadActive) { + finishedCallbacks.swap(FinishedCallbacks); + + if (Callback) { + Y_VERIFY(!ReadActive); + startCallback = std::move(Callback); + Callback = nullptr; + } else if (ReadActive) { if (ReadCallback) { readCallback = std::move(ReadCallback); ReadCallback = nullptr; @@ -780,18 +780,18 @@ private: } } - for (auto& finishedCallback : finishedCallbacks) { - auto statusCopy = status; - finishedCallback(std::move(statusCopy)); - } - - if (startCallback) { - if (status.Ok()) { - status = TGrpcStatus(grpc::StatusCode::UNKNOWN, "Unknown stream failure"); - } - startCallback(std::move(status), nullptr); - } else if (readCallback) { + for (auto& finishedCallback : finishedCallbacks) { + auto statusCopy = status; + finishedCallback(std::move(statusCopy)); + } + + if (startCallback) { if (status.Ok()) { + status = TGrpcStatus(grpc::StatusCode::UNKNOWN, "Unknown stream failure"); + } + startCallback(std::move(status), nullptr); + } else if (readCallback) { + if (status.Ok()) { status = TGrpcStatus(grpc::StatusCode::OUT_OF_RANGE, "Read EOF"); } readCallback(std::move(status)); @@ -812,7 +812,7 @@ private: TReadCallback FinishCallback; std::vector<TReadCallback> FinishedCallbacks; std::unordered_multimap<TString, TString>* InitialMetadata = nullptr; - bool Started = false; + bool Started = false; bool HasInitialMetadata = false; bool ReadActive = false; bool ReadFinished = false; @@ -821,72 +821,72 @@ private: bool FinishedOk = false; }; -template<class TRequest, class TResponse> +template<class TRequest, class TResponse> using TStreamConnectedCallback = std::function<void(TGrpcStatus&&, typename IStreamRequestReadWriteProcessor<TRequest, TResponse>::TPtr)>; - -template<class TStub, class TRequest, class TResponse> + +template<class TStub, class TRequest, class TResponse> class TStreamRequestReadWriteProcessor : public IStreamRequestReadWriteProcessor<TRequest, TResponse> , public TGRpcRequestProcessorCommon { -public: +public: using TSelf = TStreamRequestReadWriteProcessor; using TBase = IStreamRequestReadWriteProcessor<TRequest, TResponse>; - using TPtr = TIntrusivePtr<TSelf>; - using TConnectedCallback = TStreamConnectedCallback<TRequest, TResponse>; - using TReadCallback = typename TBase::TReadCallback; - using TWriteCallback = typename TBase::TWriteCallback; - using TAsyncReaderWriterPtr = std::unique_ptr<grpc::ClientAsyncReaderWriter<TRequest, TResponse>>; - using TAsyncRequest = TAsyncReaderWriterPtr (TStub::*)(grpc::ClientContext*, grpc::CompletionQueue*, void*); - + using TPtr = TIntrusivePtr<TSelf>; + using TConnectedCallback = TStreamConnectedCallback<TRequest, TResponse>; + using TReadCallback = typename TBase::TReadCallback; + using TWriteCallback = typename TBase::TWriteCallback; + using TAsyncReaderWriterPtr = std::unique_ptr<grpc::ClientAsyncReaderWriter<TRequest, TResponse>>; + using TAsyncRequest = TAsyncReaderWriterPtr (TStub::*)(grpc::ClientContext*, grpc::CompletionQueue*, void*); + explicit TStreamRequestReadWriteProcessor(TConnectedCallback&& callback) - : ConnectedCallback(std::move(callback)) - { - Y_VERIFY(ConnectedCallback, "Missing connected callback"); - } - - void Cancel() override { - Context.TryCancel(); - + : ConnectedCallback(std::move(callback)) + { + Y_VERIFY(ConnectedCallback, "Missing connected callback"); + } + + void Cancel() override { + Context.TryCancel(); + { std::unique_lock<std::mutex> guard(Mutex); - Cancelled = true; - if (Started && !(ReadFinished && WriteFinished)) { - if (!ReadActive) { - ReadFinished = true; - } - if (!WriteActive) { - WriteFinished = true; - } - if (ReadFinished && WriteFinished) { - Stream->Finish(&Status, OnFinishedTag.Prepare()); - } - } - } - } - - void Write(TRequest&& request, TWriteCallback callback) override { - TGrpcStatus status; - + Cancelled = true; + if (Started && !(ReadFinished && WriteFinished)) { + if (!ReadActive) { + ReadFinished = true; + } + if (!WriteActive) { + WriteFinished = true; + } + if (ReadFinished && WriteFinished) { + Stream->Finish(&Status, OnFinishedTag.Prepare()); + } + } + } + } + + void Write(TRequest&& request, TWriteCallback callback) override { + TGrpcStatus status; + { std::unique_lock<std::mutex> guard(Mutex); - if (Cancelled || ReadFinished || WriteFinished) { - status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Write request dropped"); - } else if (WriteActive) { - auto& item = WriteQueue.emplace_back(); - item.Callback.swap(callback); - item.Request.Swap(&request); - } else { - WriteActive = true; - WriteCallback.swap(callback); - Stream->Write(request, OnWriteDoneTag.Prepare()); - } - } - - if (!status.Ok() && callback) { - callback(std::move(status)); - } - } - + if (Cancelled || ReadFinished || WriteFinished) { + status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Write request dropped"); + } else if (WriteActive) { + auto& item = WriteQueue.emplace_back(); + item.Callback.swap(callback); + item.Request.Swap(&request); + } else { + WriteActive = true; + WriteCallback.swap(callback); + Stream->Write(request, OnWriteDoneTag.Prepare()); + } + } + + if (!status.Ok() && callback) { + callback(std::move(status)); + } + } + void ReadInitialMetadata(std::unordered_multimap<TString, TString>* metadata, TReadCallback callback) override { TGrpcStatus status; @@ -916,321 +916,321 @@ public: callback(std::move(status)); } - void Read(TResponse* message, TReadCallback callback) override { - TGrpcStatus status; - + void Read(TResponse* message, TReadCallback callback) override { + TGrpcStatus status; + { std::unique_lock<std::mutex> guard(Mutex); - Y_VERIFY(!ReadActive, "Multiple Read/Finish calls detected"); - if (!Finished) { - ReadActive = true; - ReadCallback = std::move(callback); - if (!ReadFinished) { - Stream->Read(message, OnReadDoneTag.Prepare()); - } - return; - } - if (FinishedOk) { - status = Status; - } else { - status = TGrpcStatus::Internal("Unexpected error"); - } - } - - if (status.Ok()) { - status = TGrpcStatus(grpc::StatusCode::OUT_OF_RANGE, "Read EOF"); - } - - callback(std::move(status)); - } - - void Finish(TReadCallback callback) override { - TGrpcStatus status; - + Y_VERIFY(!ReadActive, "Multiple Read/Finish calls detected"); + if (!Finished) { + ReadActive = true; + ReadCallback = std::move(callback); + if (!ReadFinished) { + Stream->Read(message, OnReadDoneTag.Prepare()); + } + return; + } + if (FinishedOk) { + status = Status; + } else { + status = TGrpcStatus::Internal("Unexpected error"); + } + } + + if (status.Ok()) { + status = TGrpcStatus(grpc::StatusCode::OUT_OF_RANGE, "Read EOF"); + } + + callback(std::move(status)); + } + + void Finish(TReadCallback callback) override { + TGrpcStatus status; + { std::unique_lock<std::mutex> guard(Mutex); - Y_VERIFY(!ReadActive, "Multiple Read/Finish calls detected"); - if (!Finished) { - ReadActive = true; - FinishCallback = std::move(callback); - if (!ReadFinished) { - ReadFinished = true; - if (!WriteActive) { - WriteFinished = true; - } - if (WriteFinished) { - Stream->Finish(&Status, OnFinishedTag.Prepare()); - } - } - return; - } - if (FinishedOk) { - status = Status; - } else { - status = TGrpcStatus::Internal("Unexpected error"); - } - } - - callback(std::move(status)); - } - - void AddFinishedCallback(TReadCallback callback) override { - Y_VERIFY(callback, "Unexpected empty callback"); - - TGrpcStatus status; - + Y_VERIFY(!ReadActive, "Multiple Read/Finish calls detected"); + if (!Finished) { + ReadActive = true; + FinishCallback = std::move(callback); + if (!ReadFinished) { + ReadFinished = true; + if (!WriteActive) { + WriteFinished = true; + } + if (WriteFinished) { + Stream->Finish(&Status, OnFinishedTag.Prepare()); + } + } + return; + } + if (FinishedOk) { + status = Status; + } else { + status = TGrpcStatus::Internal("Unexpected error"); + } + } + + callback(std::move(status)); + } + + void AddFinishedCallback(TReadCallback callback) override { + Y_VERIFY(callback, "Unexpected empty callback"); + + TGrpcStatus status; + { std::unique_lock<std::mutex> guard(Mutex); - if (!Finished) { - FinishedCallbacks.emplace_back().swap(callback); - return; - } - - if (FinishedOk) { - status = Status; - } else if (Cancelled) { - status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); - } else { - status = TGrpcStatus::Internal("Unexpected error"); - } - } - - callback(std::move(status)); - } - -private: - template<typename> friend class TServiceConnection; - - void Start(TStub& stub, TAsyncRequest asyncRequest, IQueueClientContextProvider* provider) { - auto context = provider->CreateContext(); - if (!context) { - auto callback = std::move(ConnectedCallback); - TGrpcStatus status(grpc::StatusCode::CANCELLED, "Client is shutting down"); - callback(std::move(status), nullptr); - return; - } - + if (!Finished) { + FinishedCallbacks.emplace_back().swap(callback); + return; + } + + if (FinishedOk) { + status = Status; + } else if (Cancelled) { + status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); + } else { + status = TGrpcStatus::Internal("Unexpected error"); + } + } + + callback(std::move(status)); + } + +private: + template<typename> friend class TServiceConnection; + + void Start(TStub& stub, TAsyncRequest asyncRequest, IQueueClientContextProvider* provider) { + auto context = provider->CreateContext(); + if (!context) { + auto callback = std::move(ConnectedCallback); + TGrpcStatus status(grpc::StatusCode::CANCELLED, "Client is shutting down"); + callback(std::move(status), nullptr); + return; + } + { std::unique_lock<std::mutex> guard(Mutex); - LocalContext = context; - Stream = (stub.*asyncRequest)(&Context, context->CompletionQueue(), OnConnectedTag.Prepare()); - } - - context->SubscribeStop([self = TPtr(this)] { - self->Cancel(); - }); - } - -private: - void OnConnected(bool ok) { - TConnectedCallback callback; - + LocalContext = context; + Stream = (stub.*asyncRequest)(&Context, context->CompletionQueue(), OnConnectedTag.Prepare()); + } + + context->SubscribeStop([self = TPtr(this)] { + self->Cancel(); + }); + } + +private: + void OnConnected(bool ok) { + TConnectedCallback callback; + { std::unique_lock<std::mutex> guard(Mutex); - Started = true; - if (!ok || Cancelled) { - ReadFinished = true; - WriteFinished = true; - Stream->Finish(&Status, OnFinishedTag.Prepare()); - return; - } - - callback = std::move(ConnectedCallback); - ConnectedCallback = nullptr; - } - - callback({ }, typename TBase::TPtr(this)); - } - - void OnReadDone(bool ok) { - TGrpcStatus status; - TReadCallback callback; + Started = true; + if (!ok || Cancelled) { + ReadFinished = true; + WriteFinished = true; + Stream->Finish(&Status, OnFinishedTag.Prepare()); + return; + } + + callback = std::move(ConnectedCallback); + ConnectedCallback = nullptr; + } + + callback({ }, typename TBase::TPtr(this)); + } + + void OnReadDone(bool ok) { + TGrpcStatus status; + TReadCallback callback; std::unordered_multimap<TString, TString>* initialMetadata = nullptr; - + { std::unique_lock<std::mutex> guard(Mutex); - Y_VERIFY(ReadActive, "Unexpected Read done callback"); - Y_VERIFY(!ReadFinished, "Unexpected ReadFinished flag"); - - if (!ok || Cancelled || WriteFinished) { - ReadFinished = true; - if (!WriteActive) { - WriteFinished = true; - } - if (WriteFinished) { - Stream->Finish(&Status, OnFinishedTag.Prepare()); - } - if (!ok) { - // Keep ReadActive=true, so callback is called - // after the call is finished with an error - return; - } - } - - callback = std::move(ReadCallback); - ReadCallback = nullptr; - ReadActive = false; + Y_VERIFY(ReadActive, "Unexpected Read done callback"); + Y_VERIFY(!ReadFinished, "Unexpected ReadFinished flag"); + + if (!ok || Cancelled || WriteFinished) { + ReadFinished = true; + if (!WriteActive) { + WriteFinished = true; + } + if (WriteFinished) { + Stream->Finish(&Status, OnFinishedTag.Prepare()); + } + if (!ok) { + // Keep ReadActive=true, so callback is called + // after the call is finished with an error + return; + } + } + + callback = std::move(ReadCallback); + ReadCallback = nullptr; + ReadActive = false; initialMetadata = InitialMetadata; InitialMetadata = nullptr; HasInitialMetadata = true; - } - + } + if (initialMetadata) { GetInitialMetadata(initialMetadata); } - callback(std::move(status)); - } - - void OnWriteDone(bool ok) { - TWriteCallback okCallback; - + callback(std::move(status)); + } + + void OnWriteDone(bool ok) { + TWriteCallback okCallback; + { std::unique_lock<std::mutex> guard(Mutex); - Y_VERIFY(WriteActive, "Unexpected Write done callback"); - Y_VERIFY(!WriteFinished, "Unexpected WriteFinished flag"); - - if (ok) { - okCallback.swap(WriteCallback); - } else if (WriteCallback) { - // Put callback back on the queue until OnFinished - auto& item = WriteQueue.emplace_front(); - item.Callback.swap(WriteCallback); - } - - if (!ok || Cancelled) { - WriteActive = false; - WriteFinished = true; - if (!ReadActive) { - ReadFinished = true; - } - if (ReadFinished) { - Stream->Finish(&Status, OnFinishedTag.Prepare()); - } + Y_VERIFY(WriteActive, "Unexpected Write done callback"); + Y_VERIFY(!WriteFinished, "Unexpected WriteFinished flag"); + + if (ok) { + okCallback.swap(WriteCallback); + } else if (WriteCallback) { + // Put callback back on the queue until OnFinished + auto& item = WriteQueue.emplace_front(); + item.Callback.swap(WriteCallback); + } + + if (!ok || Cancelled) { + WriteActive = false; + WriteFinished = true; + if (!ReadActive) { + ReadFinished = true; + } + if (ReadFinished) { + Stream->Finish(&Status, OnFinishedTag.Prepare()); + } } else if (!WriteQueue.empty()) { - WriteCallback.swap(WriteQueue.front().Callback); - Stream->Write(WriteQueue.front().Request, OnWriteDoneTag.Prepare()); - WriteQueue.pop_front(); - } else { - WriteActive = false; - if (ReadFinished) { - WriteFinished = true; - Stream->Finish(&Status, OnFinishedTag.Prepare()); - } - } - } - - if (okCallback) { - okCallback(TGrpcStatus()); - } - } - - void OnFinished(bool ok) { - TGrpcStatus status; + WriteCallback.swap(WriteQueue.front().Callback); + Stream->Write(WriteQueue.front().Request, OnWriteDoneTag.Prepare()); + WriteQueue.pop_front(); + } else { + WriteActive = false; + if (ReadFinished) { + WriteFinished = true; + Stream->Finish(&Status, OnFinishedTag.Prepare()); + } + } + } + + if (okCallback) { + okCallback(TGrpcStatus()); + } + } + + void OnFinished(bool ok) { + TGrpcStatus status; std::deque<TWriteItem> writesDropped; std::vector<TReadCallback> finishedCallbacks; - TConnectedCallback connectedCallback; - TReadCallback readCallback; - TReadCallback finishCallback; - + TConnectedCallback connectedCallback; + TReadCallback readCallback; + TReadCallback finishCallback; + { std::unique_lock<std::mutex> guard(Mutex); - Finished = true; - FinishedOk = ok; - LocalContext.reset(); - - if (ok) { - status = Status; - } else if (Cancelled) { - status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); - } else { - status = TGrpcStatus::Internal("Unexpected error"); - } - - writesDropped.swap(WriteQueue); - finishedCallbacks.swap(FinishedCallbacks); - - if (ConnectedCallback) { - Y_VERIFY(!ReadActive); - connectedCallback = std::move(ConnectedCallback); - ConnectedCallback = nullptr; - } else if (ReadActive) { - if (ReadCallback) { - readCallback = std::move(ReadCallback); - ReadCallback = nullptr; - } else { - finishCallback = std::move(FinishCallback); - FinishCallback = nullptr; - } - ReadActive = false; - } - } - - for (auto& item : writesDropped) { - if (item.Callback) { - TGrpcStatus writeStatus = status; - if (writeStatus.Ok()) { - writeStatus = TGrpcStatus(grpc::StatusCode::CANCELLED, "Write request dropped"); - } - item.Callback(std::move(writeStatus)); - } - } - - for (auto& finishedCallback : finishedCallbacks) { - TGrpcStatus statusCopy = status; - finishedCallback(std::move(statusCopy)); - } - - if (connectedCallback) { - if (status.Ok()) { - status = TGrpcStatus(grpc::StatusCode::UNKNOWN, "Unknown stream failure"); - } - connectedCallback(std::move(status), nullptr); - } else if (readCallback) { - if (status.Ok()) { - status = TGrpcStatus(grpc::StatusCode::OUT_OF_RANGE, "Read EOF"); - } - readCallback(std::move(status)); - } else if (finishCallback) { - finishCallback(std::move(status)); - } - } - -private: - struct TWriteItem { - TWriteCallback Callback; - TRequest Request; - }; - -private: - using TFixedEvent = TQueueClientFixedEvent<TSelf>; - - TFixedEvent OnConnectedTag = { this, &TSelf::OnConnected }; - TFixedEvent OnReadDoneTag = { this, &TSelf::OnReadDone }; - TFixedEvent OnWriteDoneTag = { this, &TSelf::OnWriteDone }; - TFixedEvent OnFinishedTag = { this, &TSelf::OnFinished }; - -private: + Finished = true; + FinishedOk = ok; + LocalContext.reset(); + + if (ok) { + status = Status; + } else if (Cancelled) { + status = TGrpcStatus(grpc::StatusCode::CANCELLED, "Stream cancelled"); + } else { + status = TGrpcStatus::Internal("Unexpected error"); + } + + writesDropped.swap(WriteQueue); + finishedCallbacks.swap(FinishedCallbacks); + + if (ConnectedCallback) { + Y_VERIFY(!ReadActive); + connectedCallback = std::move(ConnectedCallback); + ConnectedCallback = nullptr; + } else if (ReadActive) { + if (ReadCallback) { + readCallback = std::move(ReadCallback); + ReadCallback = nullptr; + } else { + finishCallback = std::move(FinishCallback); + FinishCallback = nullptr; + } + ReadActive = false; + } + } + + for (auto& item : writesDropped) { + if (item.Callback) { + TGrpcStatus writeStatus = status; + if (writeStatus.Ok()) { + writeStatus = TGrpcStatus(grpc::StatusCode::CANCELLED, "Write request dropped"); + } + item.Callback(std::move(writeStatus)); + } + } + + for (auto& finishedCallback : finishedCallbacks) { + TGrpcStatus statusCopy = status; + finishedCallback(std::move(statusCopy)); + } + + if (connectedCallback) { + if (status.Ok()) { + status = TGrpcStatus(grpc::StatusCode::UNKNOWN, "Unknown stream failure"); + } + connectedCallback(std::move(status), nullptr); + } else if (readCallback) { + if (status.Ok()) { + status = TGrpcStatus(grpc::StatusCode::OUT_OF_RANGE, "Read EOF"); + } + readCallback(std::move(status)); + } else if (finishCallback) { + finishCallback(std::move(status)); + } + } + +private: + struct TWriteItem { + TWriteCallback Callback; + TRequest Request; + }; + +private: + using TFixedEvent = TQueueClientFixedEvent<TSelf>; + + TFixedEvent OnConnectedTag = { this, &TSelf::OnConnected }; + TFixedEvent OnReadDoneTag = { this, &TSelf::OnReadDone }; + TFixedEvent OnWriteDoneTag = { this, &TSelf::OnWriteDone }; + TFixedEvent OnFinishedTag = { this, &TSelf::OnFinished }; + +private: std::mutex Mutex; - TAsyncReaderWriterPtr Stream; - TConnectedCallback ConnectedCallback; - TReadCallback ReadCallback; - TReadCallback FinishCallback; + TAsyncReaderWriterPtr Stream; + TConnectedCallback ConnectedCallback; + TReadCallback ReadCallback; + TReadCallback FinishCallback; std::vector<TReadCallback> FinishedCallbacks; std::deque<TWriteItem> WriteQueue; - TWriteCallback WriteCallback; + TWriteCallback WriteCallback; std::unordered_multimap<TString, TString>* InitialMetadata = nullptr; - bool Started = false; + bool Started = false; bool HasInitialMetadata = false; - bool ReadActive = false; - bool ReadFinished = false; - bool WriteActive = false; - bool WriteFinished = false; - bool Finished = false; - bool Cancelled = false; - bool FinishedOk = false; -}; - + bool ReadActive = false; + bool ReadFinished = false; + bool WriteActive = false; + bool WriteFinished = false; + bool Finished = false; + bool Cancelled = false; + bool FinishedOk = false; +}; + class TGRpcClientLow; template<typename TGRpcService> @@ -1245,9 +1245,9 @@ public: template<typename TRequest, typename TResponse> void DoRequest(const TRequest& request, TResponseCallback<TResponse> callback, - typename TSimpleRequestProcessor<TStub, TRequest, TResponse>::TAsyncRequest asyncRequest, + typename TSimpleRequestProcessor<TStub, TRequest, TResponse>::TAsyncRequest asyncRequest, const TCallMeta& metas = { }, - IQueueClientContextProvider* provider = nullptr) + IQueueClientContextProvider* provider = nullptr) { auto processor = MakeIntrusive<TSimpleRequestProcessor<TStub, TRequest, TResponse>>(std::move(callback)); processor->ApplyMeta(metas); @@ -1282,31 +1282,31 @@ public: processor->ApplyMeta(metas); processor->Start(*Stub_, std::move(asyncRequest), provider ? provider : Provider_); } - + /* * Start streaming response reading (one request, many responses) */ - template<typename TRequest, typename TResponse> + template<typename TRequest, typename TResponse> void DoStreamRequest(const TRequest& request, TStreamReaderCallback<TResponse> callback, typename TStreamRequestReadProcessor<TStub, TRequest, TResponse>::TAsyncRequest asyncRequest, - const TCallMeta& metas = { }, - IQueueClientContextProvider* provider = nullptr) - { + const TCallMeta& metas = { }, + IQueueClientContextProvider* provider = nullptr) + { auto processor = MakeIntrusive<TStreamRequestReadProcessor<TStub, TRequest, TResponse>>(std::move(callback)); - processor->ApplyMeta(metas); + processor->ApplyMeta(metas); processor->Start(*Stub_, request, std::move(asyncRequest), provider ? provider : Provider_); - } - + } + private: TServiceConnection(std::shared_ptr<grpc::ChannelInterface> ci, - IQueueClientContextProvider* provider) + IQueueClientContextProvider* provider) : Stub_(TGRpcService::NewStub(ci)) - , Provider_(provider) - { - Y_VERIFY(Provider_, "Connection does not have a queue provider"); - } - + , Provider_(provider) + { + Y_VERIFY(Provider_, "Connection does not have a queue provider"); + } + TServiceConnection(TStubsHolder& holder, IQueueClientContextProvider* provider) : Stub_(holder.GetOrCreateStub<TStub>()) @@ -1316,47 +1316,47 @@ private: } std::shared_ptr<TStub> Stub_; - IQueueClientContextProvider* Provider_; + IQueueClientContextProvider* Provider_; }; class TGRpcClientLow : public IQueueClientContextProvider { - class TContextImpl; - friend class TContextImpl; - - enum ECqState : TAtomicBase { - WORKING = 0, - STOP_SILENT = 1, - STOP_EXPLICIT = 2, - }; - + class TContextImpl; + friend class TContextImpl; + + enum ECqState : TAtomicBase { + WORKING = 0, + STOP_SILENT = 1, + STOP_EXPLICIT = 2, + }; + public: explicit TGRpcClientLow(size_t numWorkerThread = DEFAULT_NUM_THREADS, bool useCompletionQueuePerThread = false); ~TGRpcClientLow(); - // Tries to stop all currently running requests (via their stop callbacks) - // Will shutdown CQ and drain events once all requests have finished - // No new requests may be started after this call - void Stop(bool wait = false); - - // Waits until all currently running requests finish execution - void WaitIdle(); - - inline bool IsStopping() const { - switch (GetCqState()) { - case WORKING: - return false; - case STOP_SILENT: - case STOP_EXPLICIT: - return true; - } - - Y_UNREACHABLE(); - } - - IQueueClientContextPtr CreateContext() override; - + // Tries to stop all currently running requests (via their stop callbacks) + // Will shutdown CQ and drain events once all requests have finished + // No new requests may be started after this call + void Stop(bool wait = false); + + // Waits until all currently running requests finish execution + void WaitIdle(); + + inline bool IsStopping() const { + switch (GetCqState()) { + case WORKING: + return false; + case STOP_SILENT: + case STOP_EXPLICIT: + return true; + } + + Y_UNREACHABLE(); + } + + IQueueClientContextPtr CreateContext() override; + template<typename TGRpcService> std::unique_ptr<TServiceConnection<TGRpcService>> CreateGRpcServiceConnection(const TGRpcClientConfig& config) { return std::unique_ptr<TServiceConnection<TGRpcService>>(new TServiceConnection<TGRpcService>(CreateChannelInterface(config), this)); @@ -1367,32 +1367,32 @@ public: return std::unique_ptr<TServiceConnection<TGRpcService>>(new TServiceConnection<TGRpcService>(holder, this)); } - // Tests only, not thread-safe - void AddWorkerThreadForTest(); - + // Tests only, not thread-safe + void AddWorkerThreadForTest(); + private: using IThreadRef = std::unique_ptr<IThreadFactory::IThread>; using CompletionQueueRef = std::unique_ptr<grpc::CompletionQueue>; void Init(size_t numWorkerThread); - inline ECqState GetCqState() const { return (ECqState) AtomicGet(CqState_); } - inline void SetCqState(ECqState state) { AtomicSet(CqState_, state); } - - void StopInternal(bool silent); - void WaitInternal(); - - void ForgetContext(TContextImpl* context); - -private: + inline ECqState GetCqState() const { return (ECqState) AtomicGet(CqState_); } + inline void SetCqState(ECqState state) { AtomicSet(CqState_, state); } + + void StopInternal(bool silent); + void WaitInternal(); + + void ForgetContext(TContextImpl* context); + +private: bool UseCompletionQueuePerThread_; std::vector<CompletionQueueRef> CQS_; std::vector<IThreadRef> WorkerThreads_; - TAtomic CqState_ = -1; - + TAtomic CqState_ = -1; + std::mutex Mtx_; std::condition_variable ContextsEmpty_; std::unordered_set<TContextImpl*> Contexts_; - + std::mutex JoinMutex_; }; diff --git a/library/cpp/grpc/client/grpc_common.h b/library/cpp/grpc/client/grpc_common.h index ffcdafe045..65f74cda2f 100644 --- a/library/cpp/grpc/client/grpc_common.h +++ b/library/cpp/grpc/client/grpc_common.h @@ -24,8 +24,8 @@ struct TGRpcClientConfig { ui64 MemQuota = 0; std::unordered_map<TString, TString> StringChannelParams; std::unordered_map<TString, int> IntChannelParams; - TString LoadBalancingPolicy = { }; - TString SslTargetNameOverride = { }; + TString LoadBalancingPolicy = { }; + TString SslTargetNameOverride = { }; TGRpcClientConfig() = default; TGRpcClientConfig(const TGRpcClientConfig&) = default; @@ -68,12 +68,12 @@ inline std::shared_ptr<grpc::ChannelInterface> CreateChannelInterface(const TGRp if (mutator) { args.SetSocketMutator(mutator); } - if (!config.LoadBalancingPolicy.empty()) { - args.SetLoadBalancingPolicyName(config.LoadBalancingPolicy); - } - if (!config.SslTargetNameOverride.empty()) { - args.SetSslTargetNameOverride(config.SslTargetNameOverride); - } + if (!config.LoadBalancingPolicy.empty()) { + args.SetLoadBalancingPolicyName(config.LoadBalancingPolicy); + } + if (!config.SslTargetNameOverride.empty()) { + args.SetSslTargetNameOverride(config.SslTargetNameOverride); + } if (config.EnableSsl || config.SslCaCert) { return grpc::CreateCustomChannel(config.Locator, grpc::SslCredentials(grpc::SslCredentialsOptions{config.SslCaCert, "", ""}), args); } else { diff --git a/library/cpp/grpc/server/grpc_counters.h b/library/cpp/grpc/server/grpc_counters.h index 0b6c36c84c..e86299cbd9 100644 --- a/library/cpp/grpc/server/grpc_counters.h +++ b/library/cpp/grpc/server/grpc_counters.h @@ -83,13 +83,13 @@ public: } void CountRequestBytes(ui32 requestSize) override { - *RequestBytes += requestSize; - } - + *RequestBytes += requestSize; + } + void CountResponseBytes(ui32 responseSize) override { - *ResponseBytes += responseSize; - } - + *ResponseBytes += responseSize; + } + void StartProcessing(ui32 requestSize) override { TotalCounter->Inc(); InflyCounter->Inc(); diff --git a/library/cpp/grpc/server/grpc_request.h b/library/cpp/grpc/server/grpc_request.h index 5bd8d3902b..1fcd8b6655 100644 --- a/library/cpp/grpc/server/grpc_request.h +++ b/library/cpp/grpc/server/grpc_request.h @@ -117,24 +117,24 @@ public: return TString(this->Context.peer()); } - bool SslServer() const override { - return Server_->SslServer(); - } - + bool SslServer() const override { + return Server_->SslServer(); + } + void Run() { - // Start request unless server is shutting down - if (auto guard = Server_->ProtectShutdown()) { - Ref(); //For grpc c runtime - this->Context.AsyncNotifyWhenDone(OnFinishTag.Prepare()); - if (RequestCallback_) { - (this->Service->*RequestCallback_) - (&this->Context, Request_, - reinterpret_cast<grpc::ServerAsyncResponseWriter<TOut>*>(Writer_.Get()), this->CQ, this->CQ, GetGRpcTag()); - } else { - (this->Service->*StreamRequestCallback_) - (&this->Context, Request_, - reinterpret_cast<grpc::ServerAsyncWriter<TOut>*>(StreamWriter_.Get()), this->CQ, this->CQ, GetGRpcTag()); - } + // Start request unless server is shutting down + if (auto guard = Server_->ProtectShutdown()) { + Ref(); //For grpc c runtime + this->Context.AsyncNotifyWhenDone(OnFinishTag.Prepare()); + if (RequestCallback_) { + (this->Service->*RequestCallback_) + (&this->Context, Request_, + reinterpret_cast<grpc::ServerAsyncResponseWriter<TOut>*>(Writer_.Get()), this->CQ, this->CQ, GetGRpcTag()); + } else { + (this->Service->*StreamRequestCallback_) + (&this->Context, Request_, + reinterpret_cast<grpc::ServerAsyncWriter<TOut>*>(StreamWriter_.Get()), this->CQ, this->CQ, GetGRpcTag()); + } } } @@ -148,10 +148,10 @@ public: } void DestroyRequest() override { - if (RequestRegistered_) { - Server_->DeregisterRequestCtx(this); - RequestRegistered_ = false; - } + if (RequestRegistered_) { + Server_->DeregisterRequestCtx(this); + RequestRegistered_ = false; + } UnRef(); } @@ -346,15 +346,15 @@ private: ok ? "true" : "false", makeRequestString().data(), this->Context.peer().c_str()); if (this->Context.c_call() == nullptr) { - Y_VERIFY(!ok); + Y_VERIFY(!ok); // One ref by OnFinishTag, grpc will not call this tag if no request received UnRef(); - } else if (!(RequestRegistered_ = Server_->RegisterRequestCtx(this))) { - // Request cannot be registered due to shutdown - // It's unsafe to continue, so drop this request without processing + } else if (!(RequestRegistered_ = Server_->RegisterRequestCtx(this))) { + // Request cannot be registered due to shutdown + // It's unsafe to continue, so drop this request without processing GRPC_LOG_DEBUG(Logger_, "[%p] dropping request Name# %s due to shutdown", this, Name_); - this->Context.TryCancel(); - return false; + this->Context.TryCancel(); + return false; } Clone(); // TODO: Request pool? @@ -501,7 +501,7 @@ private: ui32 ResponseStatus = 0; THPTimer RequestTimer; TAuthState AuthState_ = 0; - bool RequestRegistered_ = false; + bool RequestRegistered_ = false; using TFixedEvent = TQueueFixedEvent<TGRpcRequestImpl>; TFixedEvent OnFinishTag = { this, &TGRpcRequestImpl::OnFinish }; diff --git a/library/cpp/grpc/server/grpc_request_base.h b/library/cpp/grpc/server/grpc_request_base.h index fcfce1c181..dc293ec01a 100644 --- a/library/cpp/grpc/server/grpc_request_base.h +++ b/library/cpp/grpc/server/grpc_request_base.h @@ -108,9 +108,9 @@ public: //! Returns peer address virtual TString GetPeer() const = 0; - - //! Returns true if server is using ssl - virtual bool SslServer() const = 0; + + //! Returns true if server is using ssl + virtual bool SslServer() const = 0; }; } // namespace NGrpc diff --git a/library/cpp/grpc/server/grpc_server.cpp b/library/cpp/grpc/server/grpc_server.cpp index 7437b7a8f5..9bc8305390 100644 --- a/library/cpp/grpc/server/grpc_server.cpp +++ b/library/cpp/grpc/server/grpc_server.cpp @@ -77,7 +77,7 @@ void TGRpcServer::Start() { builder.SetMaxReceiveMessageSize(Options_.MaxMessageSize); builder.SetMaxSendMessageSize(Options_.MaxMessageSize); for (IGRpcServicePtr service : Services_) { - service->SetServerOptions(Options_); + service->SetServerOptions(Options_); builder.RegisterService(service->GetService()); service->SetGlobalLimiterHandle(&Limiter_); } @@ -192,14 +192,14 @@ void TGRpcServer::Stop() { } for (ui64 attempt = 0; ; ++attempt) { - bool unsafe = false; + bool unsafe = false; size_t infly = 0; for (auto& service : Services_) { - unsafe |= service->IsUnsafeToShutdown(); - infly += service->RequestsInProgress(); + unsafe |= service->IsUnsafeToShutdown(); + infly += service->RequestsInProgress(); } - if (!unsafe && !infly) + if (!unsafe && !infly) break; auto spent = (TInstant::Now() - now).SecondsFloat(); @@ -208,7 +208,7 @@ void TGRpcServer::Stop() { Cerr << "GRpc shutdown warning: left infly: " << infly << ", spent: " << spent << " sec" << Endl; } - if (!unsafe && spent > Options_.GRpcShutdownDeadline.SecondsFloat()) + if (!unsafe && spent > Options_.GRpcShutdownDeadline.SecondsFloat()) break; Sleep(TDuration::MilliSeconds(10)); } diff --git a/library/cpp/grpc/server/grpc_server.h b/library/cpp/grpc/server/grpc_server.h index d6814a90a0..59ed364bc9 100644 --- a/library/cpp/grpc/server/grpc_server.h +++ b/library/cpp/grpc/server/grpc_server.h @@ -167,77 +167,77 @@ public: virtual void StopService() noexcept = 0; virtual void InitService(grpc::ServerCompletionQueue* cq, TLoggerPtr logger) = 0; virtual void SetGlobalLimiterHandle(TGlobalLimiter* limiter) = 0; - virtual bool IsUnsafeToShutdown() const = 0; - virtual size_t RequestsInProgress() const = 0; - - /** - * Called before service is added to the server builder. This allows - * service to inspect server options and initialize accordingly. - */ - virtual void SetServerOptions(const TServerOptions& options) = 0; + virtual bool IsUnsafeToShutdown() const = 0; + virtual size_t RequestsInProgress() const = 0; + + /** + * Called before service is added to the server builder. This allows + * service to inspect server options and initialize accordingly. + */ + virtual void SetServerOptions(const TServerOptions& options) = 0; }; template<typename T> class TGrpcServiceBase: public IGRpcService { public: - class TShutdownGuard { - using TOwner = TGrpcServiceBase<T>; - friend class TGrpcServiceBase<T>; - - public: - TShutdownGuard() - : Owner(nullptr) - { } - - ~TShutdownGuard() { - Release(); - } - - TShutdownGuard(TShutdownGuard&& other) - : Owner(other.Owner) - { - other.Owner = nullptr; - } - - TShutdownGuard& operator=(TShutdownGuard&& other) { - if (Y_LIKELY(this != &other)) { - Release(); - Owner = other.Owner; - other.Owner = nullptr; - } - return *this; - } - - explicit operator bool() const { - return bool(Owner); - } - - void Release() { - if (Owner) { - AtomicDecrement(Owner->GuardCount_); - Owner = nullptr; - } - } - - TShutdownGuard(const TShutdownGuard&) = delete; - TShutdownGuard& operator=(const TShutdownGuard&) = delete; - - private: - explicit TShutdownGuard(TOwner* owner) - : Owner(owner) - { } - - private: - TOwner* Owner; - }; - -public: + class TShutdownGuard { + using TOwner = TGrpcServiceBase<T>; + friend class TGrpcServiceBase<T>; + + public: + TShutdownGuard() + : Owner(nullptr) + { } + + ~TShutdownGuard() { + Release(); + } + + TShutdownGuard(TShutdownGuard&& other) + : Owner(other.Owner) + { + other.Owner = nullptr; + } + + TShutdownGuard& operator=(TShutdownGuard&& other) { + if (Y_LIKELY(this != &other)) { + Release(); + Owner = other.Owner; + other.Owner = nullptr; + } + return *this; + } + + explicit operator bool() const { + return bool(Owner); + } + + void Release() { + if (Owner) { + AtomicDecrement(Owner->GuardCount_); + Owner = nullptr; + } + } + + TShutdownGuard(const TShutdownGuard&) = delete; + TShutdownGuard& operator=(const TShutdownGuard&) = delete; + + private: + explicit TShutdownGuard(TOwner* owner) + : Owner(owner) + { } + + private: + TOwner* Owner; + }; + +public: using TCurrentGRpcService = T; void StopService() noexcept override { with_lock(Lock_) { - AtomicSet(ShuttingDown_, 1); - + AtomicSet(ShuttingDown_, 1); + // Send TryCansel to event (can be send after finishing). // Actual dtors will be called from grpc thread, so deadlock impossible for (auto* request : Requests_) { @@ -246,21 +246,21 @@ public: } } - TShutdownGuard ProtectShutdown() noexcept { - AtomicIncrement(GuardCount_); - if (IsShuttingDown()) { - AtomicDecrement(GuardCount_); - return { }; - } - - return TShutdownGuard(this); - }; - - bool IsUnsafeToShutdown() const override { - return AtomicGet(GuardCount_) > 0; - } - - size_t RequestsInProgress() const override { + TShutdownGuard ProtectShutdown() noexcept { + AtomicIncrement(GuardCount_); + if (IsShuttingDown()) { + AtomicDecrement(GuardCount_); + return { }; + } + + return TShutdownGuard(this); + }; + + bool IsUnsafeToShutdown() const override { + return AtomicGet(GuardCount_) > 0; + } + + size_t RequestsInProgress() const override { size_t c = 0; with_lock(Lock_) { c = Requests_.size(); @@ -268,9 +268,9 @@ public: return c; } - void SetServerOptions(const TServerOptions& options) override { - SslServer_ = bool(options.SslData); - NeedAuth_ = options.UseAuth; + void SetServerOptions(const TServerOptions& options) override { + SslServer_ = bool(options.SslData); + NeedAuth_ = options.UseAuth; } void SetGlobalLimiterHandle(TGlobalLimiter* /*limiter*/) override {} @@ -280,32 +280,32 @@ public: return AtomicGet(ShuttingDown_); } - bool SslServer() const { - return SslServer_; - } - + bool SslServer() const { + return SslServer_; + } + bool NeedAuth() const { return NeedAuth_; } - bool RegisterRequestCtx(ICancelableContext* req) { + bool RegisterRequestCtx(ICancelableContext* req) { with_lock(Lock_) { - auto r = Requests_.emplace(req); - Y_VERIFY(r.second, "Ctx already registered"); - - if (IsShuttingDown()) { - // Server is already shutting down - Requests_.erase(r.first); - return false; - } + auto r = Requests_.emplace(req); + Y_VERIFY(r.second, "Ctx already registered"); + + if (IsShuttingDown()) { + // Server is already shutting down + Requests_.erase(r.first); + return false; + } } - - return true; + + return true; } void DeregisterRequestCtx(ICancelableContext* req) { with_lock(Lock_) { - Y_VERIFY(Requests_.erase(req), "Ctx is not registered"); + Y_VERIFY(Requests_.erase(req), "Ctx is not registered"); } } @@ -313,15 +313,15 @@ protected: using TGrpcAsyncService = typename TCurrentGRpcService::AsyncService; TGrpcAsyncService Service_; - TGrpcAsyncService* GetService() override { + TGrpcAsyncService* GetService() override { return &Service_; } private: TAtomic ShuttingDown_ = 0; - TAtomic GuardCount_ = 0; + TAtomic GuardCount_ = 0; - bool SslServer_ = false; + bool SslServer_ = false; bool NeedAuth_ = false; THashSet<ICancelableContext*> Requests_; diff --git a/library/cpp/lfalloc/lf_allocX64.h b/library/cpp/lfalloc/lf_allocX64.h index fd2a906d6f..408fefbdc6 100644 --- a/library/cpp/lfalloc/lf_allocX64.h +++ b/library/cpp/lfalloc/lf_allocX64.h @@ -889,15 +889,15 @@ static void* SlowLFAlloc(int nSizeIdx, int blockSize, EDefrag defrag) { IncrementCounter(CT_SLOW_ALLOC_CNT, 1); TLFLockHolder ls; - for (;;) { - bool locked = ls.TryLock(&LFGlobalLock); + for (;;) { + bool locked = ls.TryLock(&LFGlobalLock); void* res = LFAllocFromCurrentChunk(nSizeIdx, blockSize, 1); if (res) { return res; // might happen when other thread allocated new current chunk } - if (locked) { - break; - } + if (locked) { + break; + } } for (;;) { uintptr_t nChunk; diff --git a/library/cpp/terminate_handler/segv_handler.cpp b/library/cpp/terminate_handler/segv_handler.cpp index f24ece4125..c5127496cd 100644 --- a/library/cpp/terminate_handler/segv_handler.cpp +++ b/library/cpp/terminate_handler/segv_handler.cpp @@ -16,7 +16,7 @@ static void SegvHandler(int sig) { Y_UNUSED(sig); const char msg[] = "Got SEGV\n"; - Y_UNUSED(write(STDERR_FILENO, msg, sizeof(msg))); + Y_UNUSED(write(STDERR_FILENO, msg, sizeof(msg))); //PrintBackTrace(); sig_t r = signal(SIGSEGV, SIG_DFL); if (r == SIG_ERR) { diff --git a/library/cpp/threading/future/core/future-inl.h b/library/cpp/threading/future/core/future-inl.h index 5fd4296a93..506c77d4c8 100644 --- a/library/cpp/threading/future/core/future-inl.h +++ b/library/cpp/threading/future/core/future-inl.h @@ -619,8 +619,8 @@ namespace NThreading { template <typename T> template <typename F> - inline TFuture<TFutureType<TFutureCallResult<F, T>>> TFuture<T>::Apply(F&& func) const { - auto promise = NewPromise<TFutureType<TFutureCallResult<F, T>>>(); + inline TFuture<TFutureType<TFutureCallResult<F, T>>> TFuture<T>::Apply(F&& func) const { + auto promise = NewPromise<TFutureType<TFutureCallResult<F, T>>>(); Subscribe([promise, func = std::forward<F>(func)](const TFuture<T>& future) mutable { NImpl::SetValue(promise, [&]() { return func(future); }); }); @@ -718,8 +718,8 @@ namespace NThreading { template <typename F> - inline TFuture<TFutureType<TFutureCallResult<F, void>>> TFuture<void>::Apply(F&& func) const { - auto promise = NewPromise<TFutureType<TFutureCallResult<F, void>>>(); + inline TFuture<TFutureType<TFutureCallResult<F, void>>> TFuture<void>::Apply(F&& func) const { + auto promise = NewPromise<TFutureType<TFutureCallResult<F, void>>>(); Subscribe([promise, func = std::forward<F>(func)](const TFuture<void>& future) mutable { NImpl::SetValue(promise, [&]() { return func(future); }); }); diff --git a/library/cpp/threading/future/core/future.h b/library/cpp/threading/future/core/future.h index 2e82bb953e..00dc245d9a 100644 --- a/library/cpp/threading/future/core/future.h +++ b/library/cpp/threading/future/core/future.h @@ -47,20 +47,20 @@ namespace NThreading { struct TFutureType<TFuture<T>> { using TType = typename TFutureType<T>::TType; }; - - template <typename F, typename T> - struct TFutureCallResult { - // NOTE: separate class for msvc compatibility - using TType = decltype(std::declval<F&>()(std::declval<const TFuture<T>&>())); - }; + + template <typename F, typename T> + struct TFutureCallResult { + // NOTE: separate class for msvc compatibility + using TType = decltype(std::declval<F&>()(std::declval<const TFuture<T>&>())); + }; } template <typename F> using TFutureType = typename NImpl::TFutureType<F>::TType; - template <typename F, typename T> - using TFutureCallResult = typename NImpl::TFutureCallResult<F, T>::TType; - + template <typename F, typename T> + using TFutureCallResult = typename NImpl::TFutureCallResult<F, T>::TType; + //! Type of the future/promise state identifier class TFutureStateId; @@ -109,7 +109,7 @@ namespace NThreading { const TFuture<T>& NoexceptSubscribe(F&& callback) const noexcept; template <typename F> - TFuture<TFutureType<TFutureCallResult<F, T>>> Apply(F&& func) const; + TFuture<TFutureType<TFutureCallResult<F, T>>> Apply(F&& func) const; TFuture<void> IgnoreResult() const; @@ -164,7 +164,7 @@ namespace NThreading { const TFuture<void>& NoexceptSubscribe(F&& callback) const noexcept; template <typename F> - TFuture<TFutureType<TFutureCallResult<F, void>>> Apply(F&& func) const; + TFuture<TFutureType<TFutureCallResult<F, void>>> Apply(F&& func) const; template <typename R> TFuture<R> Return(const R& value) const; diff --git a/library/cpp/threading/future/future_ut.cpp b/library/cpp/threading/future/future_ut.cpp index 05950a568d..377154e867 100644 --- a/library/cpp/threading/future/future_ut.cpp +++ b/library/cpp/threading/future/future_ut.cpp @@ -168,7 +168,7 @@ namespace { TTestCallback callback(123); TFuture<int> future = promise.GetFuture() - .Apply([&](const auto& theFuture) { return callback.Func(theFuture); }); + .Apply([&](const auto& theFuture) { return callback.Func(theFuture); }); promise.SetValue(456); UNIT_ASSERT_EQUAL(future.GetValue(), 123 + 456); @@ -180,7 +180,7 @@ namespace { TTestCallback callback(123); TFuture<void> future = promise.GetFuture() - .Apply([&](const auto& theFuture) { return callback.VoidFunc(theFuture); }); + .Apply([&](const auto& theFuture) { return callback.VoidFunc(theFuture); }); promise.SetValue(456); UNIT_ASSERT(future.HasValue()); @@ -191,7 +191,7 @@ namespace { TTestCallback callback(123); TFuture<int> future = promise.GetFuture() - .Apply([&](const auto& theFuture) { return callback.FutureFunc(theFuture); }); + .Apply([&](const auto& theFuture) { return callback.FutureFunc(theFuture); }); promise.SetValue(456); UNIT_ASSERT_EQUAL(future.GetValue(), 123 + 456); @@ -203,7 +203,7 @@ namespace { TTestCallback callback(123); TFuture<void> future = promise.GetFuture() - .Apply([&](const auto& theFuture) { return callback.FutureVoidFunc(theFuture); }); + .Apply([&](const auto& theFuture) { return callback.FutureVoidFunc(theFuture); }); promise.SetValue(456); UNIT_ASSERT(!future.HasValue()); diff --git a/library/cpp/threading/skip_list/skiplist_ut.cpp b/library/cpp/threading/skip_list/skiplist_ut.cpp index 52fcffda66..f1f075c04c 100644 --- a/library/cpp/threading/skip_list/skiplist_ut.cpp +++ b/library/cpp/threading/skip_list/skiplist_ut.cpp @@ -38,148 +38,148 @@ namespace NThreading { Y_UNIT_TEST_SUITE(TSkipListTest) { Y_UNIT_TEST(ShouldBeEmptyAfterCreation) { TMemoryPool pool(1024); - TSkipList<int> list(pool); + TSkipList<int> list(pool); - UNIT_ASSERT_EQUAL(list.GetSize(), 0); - } + UNIT_ASSERT_EQUAL(list.GetSize(), 0); + } Y_UNIT_TEST(ShouldAllowInsertion) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - UNIT_ASSERT(list.Insert(12345678)); - UNIT_ASSERT_EQUAL(list.GetSize(), 1); - } + UNIT_ASSERT(list.Insert(12345678)); + UNIT_ASSERT_EQUAL(list.GetSize(), 1); + } Y_UNIT_TEST(ShouldNotAllowDuplicates) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - UNIT_ASSERT(list.Insert(12345678)); - UNIT_ASSERT_EQUAL(list.GetSize(), 1); + UNIT_ASSERT(list.Insert(12345678)); + UNIT_ASSERT_EQUAL(list.GetSize(), 1); - UNIT_ASSERT(!list.Insert(12345678)); - UNIT_ASSERT_EQUAL(list.GetSize(), 1); - } + UNIT_ASSERT(!list.Insert(12345678)); + UNIT_ASSERT_EQUAL(list.GetSize(), 1); + } Y_UNIT_TEST(ShouldContainInsertedItem) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - UNIT_ASSERT(list.Insert(12345678)); - UNIT_ASSERT(list.Contains(12345678)); - } + UNIT_ASSERT(list.Insert(12345678)); + UNIT_ASSERT(list.Contains(12345678)); + } Y_UNIT_TEST(ShouldNotContainNotInsertedItem) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - UNIT_ASSERT(list.Insert(12345678)); - UNIT_ASSERT(!list.Contains(87654321)); - } + UNIT_ASSERT(list.Insert(12345678)); + UNIT_ASSERT(!list.Contains(87654321)); + } Y_UNIT_TEST(ShouldIterateAllItems) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); - - for (int i = 8; i > 0; --i) { - UNIT_ASSERT(list.Insert(i)); - } - - TSkipList<int>::TIterator it = list.SeekToFirst(); - for (int i = 1; i <= 8; ++i) { - UNIT_ASSERT(it.IsValid()); - UNIT_ASSERT_EQUAL(it.GetValue(), i); - it.Next(); - } - UNIT_ASSERT(!it.IsValid()); - } + TMemoryPool pool(1024); + TSkipList<int> list(pool); + + for (int i = 8; i > 0; --i) { + UNIT_ASSERT(list.Insert(i)); + } + + TSkipList<int>::TIterator it = list.SeekToFirst(); + for (int i = 1; i <= 8; ++i) { + UNIT_ASSERT(it.IsValid()); + UNIT_ASSERT_EQUAL(it.GetValue(), i); + it.Next(); + } + UNIT_ASSERT(!it.IsValid()); + } Y_UNIT_TEST(ShouldIterateAllItemsInReverseDirection) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); - - for (int i = 8; i > 0; --i) { - UNIT_ASSERT(list.Insert(i)); - } - - TSkipList<int>::TIterator it = list.SeekToLast(); - for (int i = 8; i > 0; --i) { - UNIT_ASSERT(it.IsValid()); - UNIT_ASSERT_EQUAL(it.GetValue(), i); - it.Prev(); - } - UNIT_ASSERT(!it.IsValid()); - } + TMemoryPool pool(1024); + TSkipList<int> list(pool); + + for (int i = 8; i > 0; --i) { + UNIT_ASSERT(list.Insert(i)); + } + + TSkipList<int>::TIterator it = list.SeekToLast(); + for (int i = 8; i > 0; --i) { + UNIT_ASSERT(it.IsValid()); + UNIT_ASSERT_EQUAL(it.GetValue(), i); + it.Prev(); + } + UNIT_ASSERT(!it.IsValid()); + } Y_UNIT_TEST(ShouldSeekToFirstItem) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - for (int i = 1; i < 10; ++i) { - UNIT_ASSERT(list.Insert(i)); - } + for (int i = 1; i < 10; ++i) { + UNIT_ASSERT(list.Insert(i)); + } - TSkipList<int>::TIterator it = list.SeekToFirst(); - UNIT_ASSERT(it.IsValid()); - UNIT_ASSERT_EQUAL(it.GetValue(), 1); - } + TSkipList<int>::TIterator it = list.SeekToFirst(); + UNIT_ASSERT(it.IsValid()); + UNIT_ASSERT_EQUAL(it.GetValue(), 1); + } Y_UNIT_TEST(ShouldSeekToLastItem) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - for (int i = 1; i < 10; ++i) { - UNIT_ASSERT(list.Insert(i)); - } + for (int i = 1; i < 10; ++i) { + UNIT_ASSERT(list.Insert(i)); + } - TSkipList<int>::TIterator it = list.SeekToLast(); - UNIT_ASSERT(it.IsValid()); - UNIT_ASSERT_EQUAL(it.GetValue(), 9); - } + TSkipList<int>::TIterator it = list.SeekToLast(); + UNIT_ASSERT(it.IsValid()); + UNIT_ASSERT_EQUAL(it.GetValue(), 9); + } Y_UNIT_TEST(ShouldSeekToExistingItem) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - UNIT_ASSERT(list.Insert(12345678)); + UNIT_ASSERT(list.Insert(12345678)); - TSkipList<int>::TIterator it = list.SeekTo(12345678); - UNIT_ASSERT(it.IsValid()); - } + TSkipList<int>::TIterator it = list.SeekTo(12345678); + UNIT_ASSERT(it.IsValid()); + } Y_UNIT_TEST(ShouldSeekAfterMissedItem) { - TMemoryPool pool(1024); - TSkipList<int> list(pool); + TMemoryPool pool(1024); + TSkipList<int> list(pool); - UNIT_ASSERT(list.Insert(100)); - UNIT_ASSERT(list.Insert(300)); + UNIT_ASSERT(list.Insert(100)); + UNIT_ASSERT(list.Insert(300)); - TSkipList<int>::TIterator it = list.SeekTo(200); - UNIT_ASSERT(it.IsValid()); - UNIT_ASSERT_EQUAL(it.GetValue(), 300); + TSkipList<int>::TIterator it = list.SeekTo(200); + UNIT_ASSERT(it.IsValid()); + UNIT_ASSERT_EQUAL(it.GetValue(), 300); - it.Prev(); - UNIT_ASSERT(it.IsValid()); - UNIT_ASSERT_EQUAL(it.GetValue(), 100); - } + it.Prev(); + UNIT_ASSERT(it.IsValid()); + UNIT_ASSERT_EQUAL(it.GetValue(), 100); + } Y_UNIT_TEST(ShouldCallDtorsOfNonPodTypes) { - UNIT_ASSERT(!TTypeTraits<TTestObject>::IsPod); - UNIT_ASSERT_EQUAL(TTestObject::Count, 0); + UNIT_ASSERT(!TTypeTraits<TTestObject>::IsPod); + UNIT_ASSERT_EQUAL(TTestObject::Count, 0); - { - TMemoryPool pool(1024); - TSkipList<TTestObject> list(pool); + { + TMemoryPool pool(1024); + TSkipList<TTestObject> list(pool); - UNIT_ASSERT(list.Insert(TTestObject(1))); - UNIT_ASSERT(list.Insert(TTestObject(2))); - - UNIT_ASSERT_EQUAL(TTestObject::Count, 2); - } + UNIT_ASSERT(list.Insert(TTestObject(1))); + UNIT_ASSERT(list.Insert(TTestObject(2))); - UNIT_ASSERT_EQUAL(TTestObject::Count, 0); - } + UNIT_ASSERT_EQUAL(TTestObject::Count, 2); + } + + UNIT_ASSERT_EQUAL(TTestObject::Count, 0); + } } } |