46ae3e4e7a
Crashes come from assertion in ReturnTokens() and new_mask() in Eventhandler. Another issue is when multiple serversockets bind to the same address, Socket will be reused to all Dart_Ports. But Socket didn't keep track of corresponding Dart Port. When one of isolates is killed, finalizer wakes up and close most recent Dart Port that sends data. After this cl, Socket may not be reused by different Dart Sockets. All Dart socket will has its own Socket object. But Sockets can have same fd. This will guarantee finalizer will close right Dart Port. Bug: https://github.com/dart-lang/sdk/issues/36106 Change-Id: Ib4620ada5f120ffda719052297009280c73b4315 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122490 Commit-Queue: Zichang Guo <zichangguo@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
271 lines
8.1 KiB
C++
271 lines
8.1 KiB
C++
// Copyright (c) 2013, 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_H_
|
|
#define RUNTIME_BIN_SOCKET_H_
|
|
|
|
#include "bin/builtin.h"
|
|
#include "bin/dartutils.h"
|
|
#include "bin/reference_counting.h"
|
|
#include "bin/socket_base.h"
|
|
#include "bin/thread.h"
|
|
#include "bin/utils.h"
|
|
#include "platform/hashmap.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
// TODO(bkonyi): Socket should also inherit from SocketBase once it is
|
|
// refactored to use instance methods when possible.
|
|
|
|
// We write Sockets into the native field of the _NativeSocket object
|
|
// on the Dart side. They are allocated in SetSocketIdNativeField(), and are
|
|
// deallocated either from the finalizer attached to _NativeSockets there, or
|
|
// from the eventhandler, whichever drops the last reference.
|
|
class Socket : public ReferenceCounted<Socket> {
|
|
public:
|
|
enum SocketRequest {
|
|
kLookupRequest = 0,
|
|
kListInterfacesRequest = 1,
|
|
kReverseLookupRequest = 2,
|
|
};
|
|
|
|
enum SocketFinalizer {
|
|
kFinalizerNormal,
|
|
kFinalizerListening,
|
|
kFinalizerStdio,
|
|
kFinalizerSignal,
|
|
};
|
|
|
|
// Keep in sync with constants in _NativeSocket in socket_patch.dart.
|
|
enum SocketType {
|
|
kTcpSocket = 18,
|
|
kUdpSocket = 19,
|
|
kInternalSocket = 20,
|
|
kInternalSignalSocket = 21,
|
|
};
|
|
|
|
explicit Socket(intptr_t fd);
|
|
|
|
intptr_t fd() const { return fd_; }
|
|
|
|
// Close fd and may need to decrement the count of handle by calling
|
|
// release().
|
|
void CloseFd();
|
|
// Set fd_ to closed. On fuchsia and win, shared socket should not
|
|
// release handle but only SetClosedFd().
|
|
void SetClosedFd();
|
|
|
|
Dart_Port isolate_port() const { return isolate_port_; }
|
|
|
|
Dart_Port port() const { return port_; }
|
|
void set_port(Dart_Port port) { port_ = port; }
|
|
|
|
uint8_t* udp_receive_buffer() const { return udp_receive_buffer_; }
|
|
void set_udp_receive_buffer(uint8_t* buffer) { udp_receive_buffer_ = buffer; }
|
|
|
|
static bool Initialize();
|
|
|
|
// Creates a socket which is bound and connected. The port to connect to is
|
|
// specified as the port component of the passed RawAddr structure.
|
|
static intptr_t CreateConnect(const RawAddr& addr);
|
|
// Creates a socket which is bound and connected. The port to connect to is
|
|
// specified as the port component of the passed RawAddr structure.
|
|
static intptr_t CreateBindConnect(const RawAddr& addr,
|
|
const RawAddr& source_addr);
|
|
// Creates a datagram socket which is bound. The port to bind
|
|
// to is specified as the port component of the RawAddr structure.
|
|
static intptr_t CreateBindDatagram(const RawAddr& addr,
|
|
bool reuseAddress,
|
|
bool reusePort,
|
|
int ttl = 1);
|
|
|
|
static CObject* LookupRequest(const CObjectArray& request);
|
|
static CObject* ListInterfacesRequest(const CObjectArray& request);
|
|
static CObject* ReverseLookupRequest(const CObjectArray& request);
|
|
|
|
static Dart_Port GetServicePort();
|
|
|
|
static void SetSocketIdNativeField(Dart_Handle handle,
|
|
intptr_t id,
|
|
SocketFinalizer finalizer);
|
|
static void ReuseSocketIdNativeField(Dart_Handle handle,
|
|
Socket* socket,
|
|
SocketFinalizer finalizer);
|
|
static Socket* GetSocketIdNativeField(Dart_Handle socket);
|
|
|
|
static bool short_socket_read() { return short_socket_read_; }
|
|
static void set_short_socket_read(bool short_socket_read) {
|
|
short_socket_read_ = short_socket_read;
|
|
}
|
|
static bool short_socket_write() { return short_socket_write_; }
|
|
static void set_short_socket_write(bool short_socket_write) {
|
|
short_socket_write_ = short_socket_write;
|
|
}
|
|
|
|
static bool IsSignalSocketFlag(intptr_t flag) {
|
|
return ((flag & (0x1 << kInternalSignalSocket)) != 0);
|
|
}
|
|
|
|
private:
|
|
~Socket() {
|
|
ASSERT(fd_ == kClosedFd);
|
|
free(udp_receive_buffer_);
|
|
udp_receive_buffer_ = NULL;
|
|
}
|
|
|
|
static const int kClosedFd = -1;
|
|
|
|
static bool short_socket_read_;
|
|
static bool short_socket_write_;
|
|
|
|
intptr_t fd_;
|
|
Dart_Port isolate_port_;
|
|
Dart_Port port_;
|
|
uint8_t* udp_receive_buffer_;
|
|
|
|
friend class ReferenceCounted<Socket>;
|
|
DISALLOW_COPY_AND_ASSIGN(Socket);
|
|
};
|
|
|
|
class ServerSocket {
|
|
public:
|
|
static const intptr_t kTemporaryFailure = -2;
|
|
|
|
static intptr_t Accept(intptr_t fd);
|
|
|
|
// Creates a socket which is bound and listens. The port to listen on is
|
|
// specified in the port component of the passed RawAddr structure.
|
|
//
|
|
// Returns a positive integer if the call is successful. In case of failure
|
|
// it returns:
|
|
//
|
|
// -1: system error (errno set)
|
|
// -5: invalid bindAddress
|
|
static intptr_t CreateBindListen(const RawAddr& addr,
|
|
intptr_t backlog,
|
|
bool v6_only = false);
|
|
|
|
// Start accepting on a newly created listening socket. If it was unable to
|
|
// start accepting incoming sockets, the fd is invalidated.
|
|
static bool StartAccept(intptr_t fd);
|
|
|
|
private:
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(ServerSocket);
|
|
};
|
|
|
|
class ListeningSocketRegistry {
|
|
public:
|
|
ListeningSocketRegistry()
|
|
: sockets_by_port_(SameIntptrValue, kInitialSocketsCount),
|
|
sockets_by_fd_(SameIntptrValue, kInitialSocketsCount),
|
|
mutex_() {}
|
|
|
|
~ListeningSocketRegistry() {
|
|
CloseAllSafe();
|
|
}
|
|
|
|
static void Initialize();
|
|
|
|
static ListeningSocketRegistry* Instance();
|
|
|
|
static void Cleanup();
|
|
|
|
// This function should be called from a dart runtime call in order to create
|
|
// a new (potentially shared) socket.
|
|
Dart_Handle CreateBindListen(Dart_Handle socket_object,
|
|
RawAddr addr,
|
|
intptr_t backlog,
|
|
bool v6_only,
|
|
bool shared);
|
|
|
|
// This should be called from the event handler for every kCloseEvent it gets
|
|
// on listening sockets.
|
|
//
|
|
// Returns `true` if the last reference has been dropped and the underlying
|
|
// socket can be closed.
|
|
//
|
|
// The caller is responsible for obtaining the mutex first, before calling
|
|
// this function.
|
|
bool CloseSafe(Socket* socketfd);
|
|
|
|
Mutex* mutex() { return &mutex_; }
|
|
|
|
private:
|
|
struct OSSocket {
|
|
RawAddr address;
|
|
int port;
|
|
bool v6_only;
|
|
bool shared;
|
|
int ref_count;
|
|
intptr_t fd;
|
|
|
|
// Singly linked lists of OSSocket instances which listen on the same port
|
|
// but on different addresses.
|
|
OSSocket* next;
|
|
|
|
OSSocket(RawAddr address,
|
|
int port,
|
|
bool v6_only,
|
|
bool shared,
|
|
Socket* socketfd)
|
|
: address(address),
|
|
port(port),
|
|
v6_only(v6_only),
|
|
shared(shared),
|
|
ref_count(0),
|
|
next(NULL) {
|
|
fd = socketfd->fd();
|
|
}
|
|
};
|
|
|
|
static const intptr_t kInitialSocketsCount = 8;
|
|
|
|
OSSocket* FindOSSocketWithAddress(OSSocket* current, const RawAddr& addr) {
|
|
while (current != NULL) {
|
|
if (SocketAddress::AreAddressesEqual(current->address, addr)) {
|
|
return current;
|
|
}
|
|
current = current->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool SameIntptrValue(void* key1, void* key2) {
|
|
return reinterpret_cast<intptr_t>(key1) == reinterpret_cast<intptr_t>(key2);
|
|
}
|
|
|
|
static uint32_t GetHashmapHashFromIntptr(intptr_t i) {
|
|
return static_cast<uint32_t>((i + 1) & 0xFFFFFFFF);
|
|
}
|
|
|
|
static void* GetHashmapKeyFromIntptr(intptr_t i) {
|
|
return reinterpret_cast<void*>(i + 1);
|
|
}
|
|
|
|
OSSocket* LookupByPort(intptr_t port);
|
|
void InsertByPort(intptr_t port, OSSocket* socket);
|
|
void RemoveByPort(intptr_t port);
|
|
|
|
OSSocket* LookupByFd(Socket* fd);
|
|
void InsertByFd(Socket* fd, OSSocket* socket);
|
|
void RemoveByFd(Socket* fd);
|
|
|
|
bool CloseOneSafe(OSSocket* os_socket, Socket* socket);
|
|
void CloseAllSafe();
|
|
|
|
SimpleHashMap sockets_by_port_;
|
|
SimpleHashMap sockets_by_fd_;
|
|
|
|
Mutex mutex_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ListeningSocketRegistry);
|
|
};
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_BIN_SOCKET_H_
|