// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_IDLE_IDLE_FILTER_STATE_H
#define GRPC_CORE_EXT_FILTERS_CLIENT_IDLE_IDLE_FILTER_STATE_H

#include <grpc/support/port_platform.h>

#include <atomic>

namespace grpc_core {

// State machine for the idle filter.
// Keeps track of how many calls are in progress, whether there is a timer
// started, and whether we've seen calls since the previous timer fired.
class IdleFilterState {
 public:
  explicit IdleFilterState(bool start_timer);
  ~IdleFilterState() = default;

  IdleFilterState(const IdleFilterState&);
  IdleFilterState& operator=(const IdleFilterState&);

  // Increment the number of calls in progress.
  void IncreaseCallCount();

  // Decrement the number of calls in progress.
  // Return true if we reached idle with no timer started.
  GRPC_MUST_USE_RESULT bool DecreaseCallCount();

  // Check if there's been any activity since the last timer check.
  // If there was, reset the activity flag and return true to indicated that
  // a new timer should be started.
  // If there was not, reset the timer flag and return false - in this case
  // we know that the channel is idle and has been for one full cycle.
  GRPC_MUST_USE_RESULT bool CheckTimer();

 private:
  // Bit in state_ indicating that the timer has been started.
  static constexpr uintptr_t kTimerStarted = 1;
  // Bit in state_ indicating that we've seen a call start or stop since the
  // last timer.
  static constexpr uintptr_t kCallsStartedSinceLastTimerCheck = 2;
  // How much should we shift to get the number of calls in progress.
  static constexpr uintptr_t kCallsInProgressShift = 2;
  // How much to increment/decrement the state_ when a call is started/stopped.
  // Ensures we don't clobber the preceding bits.
  static constexpr uintptr_t kCallIncrement = uintptr_t(1)
                                              << kCallsInProgressShift;
  std::atomic<uintptr_t> state_;
};

}  // namespace grpc_core

#endif  // GRPC_CORE_EXT_FILTERS_CLIENT_IDLE_IDLE_FILTER_STATE_H
