mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 10:03:34 +00:00
0b62bb66ed
We are using `epoll()` in edge-triggerred mode, which requires us to hit `EAGAIN` on `write()` before we are guaranteed to receive an `EPOLLOUT` event. `_SocketStreamConsumer` looks at `_NativeSocket.writeAvailable` after issuing a `write` to decide whether it should continue writing or pause and wait until event handler sends us `writeEvent`. However we did not previously set `writeAvailable` to `true` until the first `writeEvent` arrived, which lead to a hang on some Linux servers: we would write a small amount of bytes into a pipe and then wait for `writeEvent`, which would never come, as `epoll()` is only guaranteed to wake up with `EPOLLOUT` if we hit `EAGAIN` on `write()`. This CL also changes `_NativeSocket.nativeWrite` implementation to write bytes into the file descriptor until it gets EAGAIN. This reduces the number of roundtrips between Dart and native. Unfortunately this CL does not come with a regression test because it is relying on concrete `epoll()` behaviour and it this behaviour does not reproduce for me. TEST=Tested manually on the affected servers. FIXED=b/286346121 Fixed: 286346121 Change-Id: I37fef9aa12b1da724b035aa9577b414a8057217e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/308101 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Slava Egorov <vegorov@google.com>
309 lines
10 KiB
C++
309 lines
10 KiB
C++
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_BIN_SOCKET_BASE_H_
|
|
#define RUNTIME_BIN_SOCKET_BASE_H_
|
|
|
|
#include "platform/globals.h"
|
|
// Declare the OS-specific types ahead of defining the generic class.
|
|
#if defined(DART_HOST_OS_ANDROID)
|
|
#include "bin/socket_base_android.h"
|
|
#elif defined(DART_HOST_OS_FUCHSIA)
|
|
#include "bin/socket_base_fuchsia.h"
|
|
#elif defined(DART_HOST_OS_LINUX)
|
|
#include "bin/socket_base_linux.h"
|
|
#elif defined(DART_HOST_OS_MACOS)
|
|
#include "bin/socket_base_macos.h"
|
|
#elif defined(DART_HOST_OS_WINDOWS)
|
|
#include "bin/socket_base_win.h"
|
|
#else
|
|
#error Unknown target os.
|
|
#endif
|
|
|
|
#include "bin/builtin.h"
|
|
#include "bin/dartutils.h"
|
|
#include "bin/file.h"
|
|
#include "bin/thread.h"
|
|
#include "bin/utils.h"
|
|
#include "platform/allocation.h"
|
|
#include "platform/hashmap.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
union RawAddr {
|
|
struct sockaddr_in in;
|
|
struct sockaddr_in6 in6;
|
|
struct sockaddr_un un;
|
|
struct sockaddr_storage ss;
|
|
struct sockaddr addr;
|
|
};
|
|
|
|
class SocketAddress {
|
|
public:
|
|
enum {
|
|
TYPE_ANY = -1,
|
|
TYPE_IPV4,
|
|
TYPE_IPV6,
|
|
TYPE_UNIX,
|
|
};
|
|
|
|
enum {
|
|
ADDRESS_LOOPBACK_IP_V4,
|
|
ADDRESS_LOOPBACK_IP_V6,
|
|
ADDRESS_ANY_IP_V4,
|
|
ADDRESS_ANY_IP_V6,
|
|
ADDRESS_FIRST = ADDRESS_LOOPBACK_IP_V4,
|
|
ADDRESS_LAST = ADDRESS_ANY_IP_V6,
|
|
};
|
|
|
|
// Unix domain socket may be unnamed. In this case addr_.un.sun_path contains
|
|
// garbage and should not be inspected.
|
|
explicit SocketAddress(struct sockaddr* sa, bool unnamed_unix_socket = false);
|
|
|
|
~SocketAddress() {}
|
|
|
|
int GetType();
|
|
|
|
const char* as_string() const { return as_string_; }
|
|
const RawAddr& addr() const { return addr_; }
|
|
|
|
static intptr_t GetAddrLength(const RawAddr& addr,
|
|
bool unnamed_unix_socket = false);
|
|
static intptr_t GetInAddrLength(const RawAddr& addr);
|
|
static bool AreAddressesEqual(const RawAddr& a, const RawAddr& b);
|
|
static void GetSockAddr(Dart_Handle obj, RawAddr* addr);
|
|
static Dart_Handle GetUnixDomainSockAddr(const char* path,
|
|
Namespace* namespc,
|
|
RawAddr* addr);
|
|
static int16_t FromType(int type);
|
|
static void SetAddrPort(RawAddr* addr, intptr_t port);
|
|
static intptr_t GetAddrPort(const RawAddr& addr);
|
|
static Dart_Handle ToTypedData(const RawAddr& addr);
|
|
static CObjectUint8Array* ToCObject(const RawAddr& addr);
|
|
static void SetAddrScope(RawAddr* addr, intptr_t scope_id);
|
|
static intptr_t GetAddrScope(const RawAddr& addr);
|
|
|
|
private:
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
defined(DART_HOST_OS_ANDROID)
|
|
// Unix domain address is only on Linux, Mac OS and Android now.
|
|
// unix(7) require sun_path to be 108 bytes on Linux and Android, 104 bytes on
|
|
// Mac OS.
|
|
static constexpr intptr_t kMaxUnixPathLength =
|
|
sizeof(((struct sockaddr_un*)nullptr)->sun_path);
|
|
char as_string_[kMaxUnixPathLength];
|
|
#else
|
|
char as_string_[INET6_ADDRSTRLEN];
|
|
#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
// defined(DART_HOST_OS_ANDROID)
|
|
RawAddr addr_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SocketAddress);
|
|
};
|
|
|
|
class InterfaceSocketAddress {
|
|
public:
|
|
InterfaceSocketAddress(struct sockaddr* sa,
|
|
const char* interface_name,
|
|
intptr_t interface_index)
|
|
: socket_address_(new SocketAddress(sa)),
|
|
interface_name_(interface_name),
|
|
interface_index_(interface_index) {}
|
|
|
|
~InterfaceSocketAddress() { delete socket_address_; }
|
|
|
|
SocketAddress* socket_address() const { return socket_address_; }
|
|
const char* interface_name() const { return interface_name_; }
|
|
int interface_index() const { return interface_index_; }
|
|
|
|
private:
|
|
SocketAddress* socket_address_;
|
|
const char* interface_name_;
|
|
intptr_t interface_index_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(InterfaceSocketAddress);
|
|
};
|
|
|
|
template <typename T>
|
|
class AddressList {
|
|
public:
|
|
explicit AddressList(intptr_t count)
|
|
: count_(count), addresses_(new T*[count_]) {}
|
|
|
|
~AddressList() {
|
|
for (intptr_t i = 0; i < count_; i++) {
|
|
delete addresses_[i];
|
|
}
|
|
delete[] addresses_;
|
|
}
|
|
|
|
intptr_t count() const { return count_; }
|
|
T* GetAt(intptr_t i) const { return addresses_[i]; }
|
|
void SetAt(intptr_t i, T* addr) { addresses_[i] = addr; }
|
|
|
|
private:
|
|
const intptr_t count_;
|
|
T** addresses_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AddressList);
|
|
};
|
|
|
|
class SocketControlMessage {
|
|
public:
|
|
SocketControlMessage(intptr_t level,
|
|
intptr_t type,
|
|
void* data,
|
|
size_t data_length)
|
|
: level_(level), type_(type), data_(data), data_length_(data_length) {}
|
|
|
|
intptr_t level() const { return level_; }
|
|
intptr_t type() const { return type_; }
|
|
void* data() const { return data_; }
|
|
size_t data_length() const { return data_length_; }
|
|
|
|
inline bool is_file_descriptors_control_message();
|
|
|
|
private:
|
|
const intptr_t level_;
|
|
const intptr_t type_;
|
|
void* data_;
|
|
const size_t data_length_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SocketControlMessage);
|
|
};
|
|
|
|
class SocketBase : public AllStatic {
|
|
public:
|
|
enum SocketRequest {
|
|
kLookupRequest = 0,
|
|
kListInterfacesRequest = 1,
|
|
kReverseLookupRequest = 2,
|
|
};
|
|
|
|
enum SocketOpKind {
|
|
kSync,
|
|
kAsync,
|
|
};
|
|
|
|
// TODO(dart:io): Convert these to instance methods where possible.
|
|
static bool Initialize();
|
|
static intptr_t Available(intptr_t fd);
|
|
static intptr_t Read(intptr_t fd,
|
|
void* buffer,
|
|
intptr_t num_bytes,
|
|
SocketOpKind sync);
|
|
static intptr_t Write(intptr_t fd,
|
|
const void* buffer,
|
|
intptr_t num_bytes,
|
|
SocketOpKind sync);
|
|
|
|
// Send data on a socket. The port to send to is specified in the port
|
|
// component of the passed RawAddr structure. The RawAddr structure is only
|
|
// used for datagram sockets.
|
|
static intptr_t SendTo(intptr_t fd,
|
|
const void* buffer,
|
|
intptr_t num_bytes,
|
|
const RawAddr& addr,
|
|
SocketOpKind sync);
|
|
static intptr_t SendMessage(intptr_t fd,
|
|
void* buffer,
|
|
size_t buffer_num_bytes,
|
|
SocketControlMessage* messages,
|
|
intptr_t num_messages,
|
|
SocketOpKind sync,
|
|
OSError* p_oserror);
|
|
static intptr_t RecvFrom(intptr_t fd,
|
|
void* buffer,
|
|
intptr_t num_bytes,
|
|
RawAddr* addr,
|
|
SocketOpKind sync);
|
|
static intptr_t ReceiveMessage(intptr_t fd,
|
|
void* buffer,
|
|
int64_t* p_buffer_num_bytes,
|
|
SocketControlMessage** p_messages,
|
|
SocketOpKind sync,
|
|
OSError* p_oserror);
|
|
static bool AvailableDatagram(intptr_t fd, void* buffer, intptr_t num_bytes);
|
|
// Returns true if the given error-number is because the system was not able
|
|
// to bind the socket to a specific IP.
|
|
static bool IsBindError(intptr_t error_number);
|
|
static intptr_t GetPort(intptr_t fd);
|
|
static bool GetSocketName(intptr_t fd, SocketAddress* p_sa);
|
|
static SocketAddress* GetRemotePeer(intptr_t fd, intptr_t* port);
|
|
static void GetError(intptr_t fd, OSError* os_error);
|
|
static int GetType(intptr_t fd);
|
|
static intptr_t GetStdioHandle(intptr_t num);
|
|
static void Close(intptr_t fd);
|
|
static bool GetNoDelay(intptr_t fd, bool* enabled);
|
|
static bool SetNoDelay(intptr_t fd, bool enabled);
|
|
static bool GetMulticastLoop(intptr_t fd, intptr_t protocol, bool* enabled);
|
|
static bool SetMulticastLoop(intptr_t fd, intptr_t protocol, bool enabled);
|
|
static bool GetMulticastHops(intptr_t fd, intptr_t protocol, int* value);
|
|
static bool SetMulticastHops(intptr_t fd, intptr_t protocol, int value);
|
|
static bool GetBroadcast(intptr_t fd, bool* value);
|
|
static bool SetBroadcast(intptr_t fd, bool value);
|
|
static bool GetOption(intptr_t fd,
|
|
int level,
|
|
int option,
|
|
char* data,
|
|
unsigned int* length);
|
|
static bool SetOption(intptr_t fd,
|
|
int level,
|
|
int option,
|
|
const char* data,
|
|
int length);
|
|
static bool JoinMulticast(intptr_t fd,
|
|
const RawAddr& addr,
|
|
const RawAddr& interface,
|
|
int interfaceIndex);
|
|
static bool LeaveMulticast(intptr_t fd,
|
|
const RawAddr& addr,
|
|
const RawAddr& interface,
|
|
int interfaceIndex);
|
|
|
|
#if defined(DART_HOST_OS_WINDOWS)
|
|
static bool HasPendingWrite(intptr_t fd);
|
|
#endif
|
|
|
|
// Perform a hostname lookup. Returns a AddressList of SocketAddress's.
|
|
static AddressList<SocketAddress>* LookupAddress(const char* host,
|
|
int type,
|
|
OSError** os_error);
|
|
|
|
static bool ReverseLookup(const RawAddr& addr,
|
|
char* host,
|
|
intptr_t host_len,
|
|
OSError** os_error);
|
|
|
|
static bool ParseAddress(int type, const char* address, RawAddr* addr);
|
|
|
|
static bool IsValidAddress(const char* address);
|
|
|
|
// Convert address from byte representation to human readable string.
|
|
static bool RawAddrToString(RawAddr* addr, char* str);
|
|
static bool FormatNumericAddress(const RawAddr& addr, char* address, int len);
|
|
|
|
// List interfaces. Returns a AddressList of InterfaceSocketAddress's.
|
|
static AddressList<InterfaceSocketAddress>* ListInterfaces(
|
|
int type,
|
|
OSError** os_error);
|
|
|
|
private:
|
|
#if !defined(DART_HOST_OS_WINDOWS)
|
|
static intptr_t WriteImpl(intptr_t fd,
|
|
const void* buffer,
|
|
intptr_t num_bytes,
|
|
SocketOpKind sync);
|
|
#endif
|
|
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketBase);
|
|
};
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_BIN_SOCKET_BASE_H_
|