watchdog_timer.h 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. #pragma once
  2. namespace NActors {
  3. template <typename TEvent>
  4. class TWatchdogTimer {
  5. using TCallback = std::function<void()>;
  6. const TDuration Timeout;
  7. const TCallback Callback;
  8. TInstant LastResetTimestamp;
  9. TEvent* ExpectedEvent = nullptr;
  10. ui32 Iteration = 0;
  11. static constexpr ui32 NumIterationsBeforeFiring = 2;
  12. public:
  13. TWatchdogTimer(TDuration timeout, TCallback callback)
  14. : Timeout(timeout)
  15. , Callback(std::move(callback))
  16. {
  17. }
  18. void Arm(const TActorIdentity& actor) {
  19. if (Timeout != TDuration::Zero() && Timeout != TDuration::Max()) {
  20. Schedule(Timeout, actor);
  21. Reset();
  22. }
  23. }
  24. void Reset() {
  25. LastResetTimestamp = TActivationContext::Now();
  26. }
  27. void Disarm() {
  28. ExpectedEvent = nullptr;
  29. }
  30. void operator()(typename TEvent::TPtr& ev) {
  31. if (ev->Get() == ExpectedEvent) {
  32. const TInstant now = TActivationContext::Now();
  33. const TInstant barrier = LastResetTimestamp + Timeout;
  34. if (now < barrier) {
  35. // the time hasn't come yet
  36. Schedule(barrier - now, TActorIdentity(ev->Recipient));
  37. } else if (Iteration < NumIterationsBeforeFiring) {
  38. // time has come, but we will still give actor a chance to process some messages and rearm timer
  39. ++Iteration;
  40. TActivationContext::Send(ev.Release()); // send this event into queue once more
  41. } else {
  42. // no chance to disarm, fire callback
  43. Callback();
  44. ExpectedEvent = nullptr;
  45. Iteration = 0;
  46. }
  47. }
  48. }
  49. private:
  50. void Schedule(TDuration timeout, const TActorIdentity& actor) {
  51. auto ev = MakeHolder<TEvent>();
  52. ExpectedEvent = ev.Get();
  53. Iteration = 0;
  54. actor.Schedule(timeout, ev.Release());
  55. }
  56. };
  57. }