// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_QUIC_IO_QUIC_EVENT_LOOP_H_
#define QUICHE_QUIC_IO_QUIC_EVENT_LOOP_H_

#include <cstdint>
#include <memory>

#include "absl/base/attributes.h"
#include "quiche/quic/core/io/socket.h"
#include "quiche/quic/core/quic_alarm_factory.h"
#include "quiche/quic/core/quic_clock.h"

namespace quic {

// A bitmask indicating a set of I/O events.
using QuicSocketEventMask = uint8_t;
inline constexpr QuicSocketEventMask kSocketEventReadable = 0x01;
inline constexpr QuicSocketEventMask kSocketEventWritable = 0x02;
inline constexpr QuicSocketEventMask kSocketEventError = 0x04;

class QuicEventLoop;

// A listener associated with a file descriptor.
class QuicSocketEventListener {
 public:
  virtual ~QuicSocketEventListener() = default;

  virtual void OnSocketEvent(QuicEventLoop* event_loop, SocketFd fd,
                             QuicSocketEventMask events) = 0;
};

// An abstraction for an event loop that can handle alarms and notify the
// listener about I/O events occuring to the registered UDP sockets.
//
// Note on error handling: while most of the methods below return a boolean to
// indicate whether the operation has succeeded or not, some will QUIC_BUG
// instead.
class QuicEventLoop {
 public:
  virtual ~QuicEventLoop() = default;

  // Indicates whether the event loop implementation supports edge-triggered
  // notifications.  If true, all of the events are permanent and are notified
  // as long as they are registered.  If false, whenever an event is triggered,
  // the event registration is unset and has to be re-armed using RearmSocket().
  virtual bool SupportsEdgeTriggered() const = 0;

  // Watches for all of the requested |events| that occur on the |fd| and
  // notifies the |listener| about them.  |fd| must not be already registered;
  // if it is, the function returns false.  The |listener| must be alive for as
  // long as it is registered.
  virtual ABSL_MUST_USE_RESULT bool RegisterSocket(
      SocketFd fd, QuicSocketEventMask events,
      QuicSocketEventListener* listener) = 0;
  // Removes the listener associated with |fd|.  Returns false if the listener
  // is not found.
  virtual ABSL_MUST_USE_RESULT bool UnregisterSocket(SocketFd fd) = 0;
  // Adds |events| to the list of the listened events for |fd|, given that |fd|
  // is already registered.  Must be only called if SupportsEdgeTriggered() is
  // false.
  virtual ABSL_MUST_USE_RESULT bool RearmSocket(SocketFd fd,
                                                QuicSocketEventMask events) = 0;
  // Causes the |fd| to be notified of |events| on the next event loop iteration
  // even if none of the specified events has happened.
  virtual ABSL_MUST_USE_RESULT bool ArtificiallyNotifyEvent(
      SocketFd fd, QuicSocketEventMask events) = 0;

  // Runs a single iteration of the event loop.  The iteration will run for at
  // most |default_timeout|.
  virtual void RunEventLoopOnce(QuicTime::Delta default_timeout) = 0;

  // Returns an alarm factory that allows alarms to be scheduled on this event
  // loop.
  virtual std::unique_ptr<QuicAlarmFactory> CreateAlarmFactory() = 0;

  // Returns the clock that is used by the alarm factory that the event loop
  // provides.
  virtual const QuicClock* GetClock() = 0;
};

// A factory object for the event loop. Every implementation is expected to have
// a static singleton instance.
class QuicEventLoopFactory {
 public:
  virtual ~QuicEventLoopFactory() {}

  // Creates an event loop.  Note that |clock| may be ignored if the event loop
  // implementation uses its own clock internally.
  virtual std::unique_ptr<QuicEventLoop> Create(QuicClock* clock) = 0;

  // A human-readable name of the event loop implementation used in diagnostics
  // output.
  virtual std::string GetName() const = 0;
};

}  // namespace quic

#endif  // QUICHE_QUIC_IO_QUIC_EVENT_LOOP_H_
