text: // Copyright (c) 2019 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_QBONE_PLATFORM_KERNEL_INTERFACE_H_
  #define QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_
  
  #include <errno.h>
  #include <fcntl.h>
  #include <sys/ioctl.h>
  #include <sys/socket.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #include <type_traits>
  #include <utility>
  
  namespace quic {
  
  // A wrapper for making syscalls to the kernel, so that syscalls can be
  // mocked during testing.
  class KernelInterface {
   public:
    virtual ~KernelInterface() {}
    virtual int bind(int fd, const struct sockaddr* addr, socklen_t addr_len) = 0;
    virtual int close(int fd) = 0;
    virtual int ioctl(int fd, int request, void* argp) = 0;
    virtual int open(const char* pathname, int flags) = 0;
    virtual ssize_t read(int fd, void* buf, size_t count) = 0;
    virtual ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags,
                             struct sockaddr* src_addr, socklen_t* addrlen) = 0;
    virtual ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) = 0;
    virtual ssize_t sendto(int sockfd, const void* buf, size_t len, int flags,
                           const struct sockaddr* dest_addr,
                           socklen_t addrlen) = 0;
    virtual int socket(int domain, int type, int protocol) = 0;
    virtual int setsockopt(int fd, int level, int optname, const void* optval,
                           socklen_t optlen) = 0;
    virtual ssize_t write(int fd, const void* buf, size_t count) = 0;
  };
  
  // It is unfortunate to have R here, but std::result_of cannot be used.
  template <typename F, typename R, typename... Params>
  auto SyscallRetryOnError(R r, F f, Params&&... params)
      -> decltype(f(std::forward<Params>(params)...)) {
    static_assert(
        std::is_same<decltype(f(std::forward<Params>(params)...)), R>::value,
        "Return type does not match");
    decltype(f(std::forward<Params>(params)...)) result;
    do {
      result = f(std::forward<Params>(params)...);
    } while (result == r && errno == EINTR);
    return result;
  }
  
  template <typename F, typename... Params>
  auto SyscallRetry(F f, Params&&... params)
      -> decltype(f(std::forward<Params>(params)...)) {
    return SyscallRetryOnError(-1, f, std::forward<Params>(params)...);
  }
  
  template <typename Runner>
  class ParametrizedKernel final : public KernelInterface {
   public:
    static_assert(std::is_trivially_destructible<Runner>::value,
                  "Runner is used as static, must be trivially destructible");
  
    ~ParametrizedKernel() override {}
  
    int bind(int fd, const struct sockaddr* addr, socklen_t addr_len) override {
      static Runner syscall("bind");
      return syscall.Retry(&::bind, fd, addr, addr_len);
    }
    int close(int fd) override {
      static Runner syscall("close");
      return syscall.Retry(&::close, fd);
    }
    int ioctl(int fd, int request, void* argp) override {
      static Runner syscall("ioctl");
      return syscall.Retry(&::ioctl, fd, request, argp);
    }
    int open(const char* pathname, int flags) override {
      static Runner syscall("open");
      return syscall.Retry(&::open, pathname, flags);
    }
    ssize_t read(int fd, void* buf, size_t count) override {
      static Runner syscall("read");
      return syscall.Run(&::read, fd, buf, count);
    }
    ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags,
                     struct sockaddr* src_addr, socklen_t* addrlen) override {
      static Runner syscall("recvfrom");
      return syscall.RetryOnError(&::recvfrom, static_cast<ssize_t>(-1), sockfd,
                                  buf, len, flags, src_addr, addrlen);
    }
    ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) override {
      static Runner syscall("sendmsg");
      return syscall.RetryOnError(&::sendmsg, static_cast<ssize_t>(-1), sockfd,
                                  msg, flags);
    }
    ssize_t sendto(int sockfd, const void* buf, size_t len, int flags,
                   const struct sockaddr* dest_addr, socklen_t addrlen) override {
      static Runner syscall("sendto");
      return syscall.RetryOnError(&::sendto, static_cast<ssize_t>(-1), sockfd,
                                  buf, len, flags, dest_addr, addrlen);
    }
    int socket(int domain, int type, int protocol) override {
      static Runner syscall("socket");
      return syscall.Retry(&::socket, domain, type, protocol);
    }
    int setsockopt(int fd, int level, int optname, const void* optval,
                   socklen_t optlen) override {
      static Runner syscall("setsockopt");
      return syscall.Retry(&::setsockopt, fd, level, optname, optval, optlen);
    }
    ssize_t write(int fd, const void* buf, size_t count) override {
      static Runner syscall("write");
      return syscall.Run(&::write, fd, buf, count);
    }
  };
  
  class DefaultKernelRunner {
   public:
    explicit DefaultKernelRunner(const char* name) {}
  
    template <typename F, typename R, typename... Params>
    static auto RetryOnError(F f, R r, Params&&... params)
        -> decltype(f(std::forward<Params>(params)...)) {
      return SyscallRetryOnError(r, f, std::forward<Params>(params)...);
    }
  
    template <typename F, typename... Params>
    static auto Retry(F f, Params&&... params)
        -> decltype(f(std::forward<Params>(params)...)) {
      return SyscallRetry(f, std::forward<Params>(params)...);
    }
  
    template <typename F, typename... Params>
    static auto Run(F f, Params&&... params)
        -> decltype(f(std::forward<Params>(params)...)) {
      return f(std::forward<Params>(params)...);
    }
  };
  
  using Kernel = ParametrizedKernel<DefaultKernelRunner>;
  
  }  // namespace quic
  
  #endif  // QUICHE_QUIC_QBONE_PLATFORM_KERNEL_INTERFACE_H_
  