// Copyright 2014 The Android Open Source Project
//
// 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.

#pragma once

#include "aemu/base/Compiler.h"

#include <stdint.h>

namespace android {
namespace base {

// A SocketWaiter is an abstraction that allows one to wait for i/o
// events on a set of socket file descriptors for a certain amount
// of time. Usage is:
//
//  1/ Create the waiter instance:
//
//       SocketWaiter* waiter = SocketWaiter::create();
//
//  2/ Call the update() method to indicate which I/O events you want
//     to listen to for a given file descriptor |fd|:
//
//       waiter->update(fd1, SocketWaiter::kEventRead);
//       waiter->update(fd2, SocketWaiter::kEventWrite);
//
//  3/ Call the wait() method to wait for i/o events on the registered
//     socket file descriptors.
//
//       int ret = waiter->wait(timeout_ms);
//
//  4/ After wait() returns, call nextPendingFd() to get the list of
//     file descriptors that have pending i/o events, and the corresponding
//     event mask. The function returns -1 once you reach past the list:
//
//       for (;;) {
//          unsigned events = 0;
//          int fd = waiter->nextPendingFd(&events);
//          if (fd < 0) {
//            break;
//          }
//          // Handle |events| on socket |fd|.
//       }
//
//  5/ Which file descriptors are being watched is recorded by the waiter
//     between several calls to wait(). You can use reset() to clear all
//     of them at once, or call update() with 0 as the second parameter to
//     indicate you no longer want to listen to events on a given
//     descriptor, as in:
//
//        waiter->update(fd2, 0);
//
class SocketWaiter {
public:
    enum Event {
        kEventRead = (1U << 0),
        kEventWrite = (1U << 1),
    };

    // Create new SocketWaiter instance.
    static SocketWaiter* create();

    // Destroy the instance.
    virtual ~SocketWaiter() {}

    // Reset the waiter, i.e. equivalent to calling update(fd, 0) on
    // all previously registered socket descriptors.
    virtual void reset() = 0;

    // Return the current wanted event bitmask for a given |fd|.
    virtual unsigned wantedEventsFor(int fd) const = 0;

    // Return the pending event bitmask for a given |fd|. Only use this
    // after calling wait().
    virtual unsigned pendingEventsFor(int fd) const = 0;

    // Return true iff there are registered socket descriptors.
    virtual bool hasFds() const = 0;

    // Tell the waiter to look for i/o events on socket descriptor |fd|.
    // |events| is a bitmask containing kEventRead, kEventWrite or both.
    virtual void update(int fd, unsigned events) = 0;

    // Wait at most |timeout_ms| milli-seconds for i/o events to occur
    // on registered socket descriptors. If |timeout_ms| is 0, return
    // immediately after polling the descriptors. If it is INT64_MAX,
    // then wait indefinitely.
    //
    // Return the number of descriptors that have i/o events.
    // In case of timeout, return 0 and sets errno to ETIMEDOUT.
    // In case of error, return -1/errno.
    //
    // This function loops around EINTR as a convenience.
    virtual int wait(int64_t timeout_ms) = 0;

    // Get the next pending socket descriptor and associated i/o event mask.
    // Return the next socket descriptor, or -1 when the end of the list
    // is reached. On success, sets |*fdEvents| to the corresponding
    // event mask.
    virtual int nextPendingFd(unsigned* fdEvents) = 0;

protected:
    SocketWaiter() {}

    DISALLOW_COPY_AND_ASSIGN(SocketWaiter);
};

}  // namespace base
}  // namespace android
