diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index 4b78b7bb90..12fe06b7a8 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2009, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef NET_SOCKET_H @@ -38,7 +38,6 @@ typedef struct net_socket { } send, receive; status_t error; - struct net_socket *parent; } net_socket; struct net_socket_module_info { @@ -47,7 +46,7 @@ struct net_socket_module_info { status_t (*open_socket)(int family, int type, int protocol, net_socket **_socket); status_t (*close)(net_socket *socket); - status_t (*free)(net_socket *socket); + void (*free)(net_socket *socket); status_t (*readv)(net_socket *socket, const iovec *vecs, size_t vecCount, size_t *_length); @@ -72,13 +71,17 @@ struct net_socket_module_info { struct net_stat *stat); // connections + void (*acquire_socket)(net_socket *socket); + bool (*release_socket)(net_socket *socket); + status_t (*spawn_pending_socket)(net_socket *parent, net_socket **_socket); - void (*delete_socket)(net_socket *socket); status_t (*dequeue_connected)(net_socket *parent, net_socket **_socket); ssize_t (*count_connected)(net_socket *parent); status_t (*set_max_backlog)(net_socket *socket, uint32 backlog); + bool (*has_parent)(net_socket *socket); status_t (*set_connected)(net_socket *socket); + status_t (*set_aborted)(net_socket *socket); // notifications status_t (*request_notification)(net_socket *socket, uint8 event, diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp index 98376f4501..27613ad03e 100644 --- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp @@ -318,7 +318,9 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) TCPEndpoint *endpoint = _LookupConnection(local, peer); if (endpoint != NULL) { - TRACE(("TCP: Received packet corresponds to explicit endpoint %p\n", endpoint)); + TRACE(("TCP: Received packet corresponds to explicit endpoint %p\n", + endpoint)); + gSocketModule->acquire_socket(endpoint->socket); return endpoint; } @@ -329,7 +331,9 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) endpoint = _LookupConnection(local, *wildcard); if (endpoint != NULL) { - TRACE(("TCP: Received packet corresponds to wildcard endpoint %p\n", endpoint)); + TRACE(("TCP: Received packet corresponds to wildcard endpoint %p\n", + endpoint)); + gSocketModule->acquire_socket(endpoint->socket); return endpoint; } @@ -339,7 +343,9 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) endpoint = _LookupConnection(*localWildcard, *wildcard); if (endpoint != NULL) { - TRACE(("TCP: Received packet corresponds to local wildcard endpoint %p\n", endpoint)); + TRACE(("TCP: Received packet corresponds to local wildcard endpoint " + "%p\n", endpoint)); + gSocketModule->acquire_socket(endpoint->socket); return endpoint; } @@ -564,7 +570,7 @@ void EndpointManager::Dump() const { kprintf("-------- TCP Domain %p ---------\n", this); - kprintf("%10s %20s %20s %8s %8s %12s\n", "address", "local", "peer", + kprintf("%10s %21s %21s %8s %8s %12s\n", "address", "local", "peer", "recv-q", "send-q", "state"); ConnectionTable::Iterator iterator = fConnectionHash.GetIterator(); @@ -576,7 +582,7 @@ EndpointManager::Dump() const endpoint->LocalAddress().AsString(localBuf, sizeof(localBuf), true); endpoint->PeerAddress().AsString(peerBuf, sizeof(peerBuf), true); - kprintf("%p %20s %20s %8lu %8lu %12s\n", endpoint, localBuf, peerBuf, + kprintf("%p %21s %21s %8lu %8lu %12s\n", endpoint, localBuf, peerBuf, endpoint->fReceiveQueue.Available(), endpoint->fSendQueue.Used(), name_for_state(endpoint->State())); } diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index c6ec676e96..49a8c6462f 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -554,7 +554,7 @@ TCPEndpoint::Close() } -status_t +void TCPEndpoint::Free() { TRACE("Free()"); @@ -562,17 +562,16 @@ TCPEndpoint::Free() MutexLocker _(fLock); if (fState <= SYNCHRONIZE_SENT) - return B_OK; + return; // we are only interested in the timer, not in changing state _EnterTimeWait(); fFlags |= FLAG_CLOSED; - if ((fFlags & FLAG_DELETE_ON_CLOSE) != 0) - return B_OK; - - return B_BUSY; + if ((fFlags & FLAG_DELETE_ON_CLOSE) == 0) { // we'll be freed later when the 2MSL timer expires + gSocketModule->acquire_socket(socket); + } } @@ -1169,7 +1168,7 @@ TCPEndpoint::_MarkEstablished() fState = ESTABLISHED; T(State(this)); - if (socket->parent != NULL) { + if (gSocketModule->has_parent(socket)) { gSocketModule->set_connected(socket); release_sem_etc(fAcceptSemaphore, 1, B_DO_NOT_RESCHEDULE); } @@ -1211,13 +1210,11 @@ TCPEndpoint::_Close() fSendList.Signal(); _NotifyReader(); - if (socket->parent != NULL) { + if (gSocketModule->has_parent(socket)) { // We still have a parent - obviously, we haven't been accepted yet, // so no one could ever close us. - // Since we can't just delete ourself here, we trigger an immediate - // time-wait timer. _CancelConnectionTimers(); - gStackModule->set_timer(&fTimeWaitTimer, 0); + gSocketModule->set_aborted(socket); } } @@ -1792,8 +1789,7 @@ TCPEndpoint::SegmentReceived(tcp_segment_header& segment, net_buffer* buffer) if ((fFlags & (FLAG_CLOSED | FLAG_DELETE_ON_CLOSE)) == (FLAG_CLOSED | FLAG_DELETE_ON_CLOSE)) { locker.Unlock(); - gSocketModule->delete_socket(socket); - // this will also delete us + gSocketModule->release_socket(socket); } return segmentAction; @@ -2293,7 +2289,7 @@ TCPEndpoint::_TimeWaitTimer(net_timer* timer, void* _endpoint) locker.Unlock(); - gSocketModule->delete_socket(endpoint->socket); + gSocketModule->release_socket(endpoint->socket); } diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h index 2ea07c1100..9d459201bc 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h @@ -50,7 +50,7 @@ public: status_t Open(); status_t Close(); - status_t Free(); + void Free(); status_t Connect(const struct sockaddr* address); status_t Accept(struct net_socket** _acceptedSocket); status_t Bind(const sockaddr* address); diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 0df9eed324..1cc946acf8 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -496,7 +496,8 @@ tcp_close(net_protocol* protocol) status_t tcp_free(net_protocol* protocol) { - return ((TCPEndpoint*)protocol)->Free(); + ((TCPEndpoint*)protocol)->Free(); + return B_OK; } @@ -708,9 +709,10 @@ tcp_receive_data(net_buffer* buffer) TCPEndpoint* endpoint = endpointManager->FindConnection( buffer->destination, buffer->source); - if (endpoint != NULL) + if (endpoint != NULL) { segmentAction = endpoint->SegmentReceived(segment, buffer); - else if ((segment.flags & TCP_FLAG_RESET) == 0) + gSocketModule->release_socket(endpoint->socket); + } else if ((segment.flags & TCP_FLAG_RESET) == 0) segmentAction = DROP | RESET; if ((segmentAction & RESET) != 0) { diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index bac08b9df2..9c5452c552 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -16,13 +16,15 @@ #include -#include #include #include #include + +#include #include #include #include +#include #include #include @@ -41,8 +43,15 @@ struct net_socket_private; typedef DoublyLinkedList SocketList; -struct net_socket_private - : net_socket, DoublyLinkedListLinkImpl { +struct net_socket_private : net_socket, + DoublyLinkedListLinkImpl, + WeakReferenceable { + net_socket_private(); + ~net_socket_private(); + + void RemoveFromParent(); + + WeakPointer* parent; team_id owner; uint32 max_backlog; uint32 child_count; @@ -56,7 +65,6 @@ struct net_socket_private }; -void socket_delete(net_socket* socket); int socket_bind(net_socket* socket, const struct sockaddr* address, socklen_t addressLength); int socket_setsockopt(net_socket* socket, int level, int option, @@ -67,6 +75,80 @@ static SocketList sSocketList; static mutex sSocketLock; +net_socket_private::net_socket_private() + : WeakReferenceable(this), + parent(NULL), + owner(-1), + max_backlog(0), + child_count(0), + select_pool(NULL), + is_connected(false) +{ + first_protocol = NULL; + first_info = NULL; + options = 0; + linger = 0; + bound_to_device = 0; + error = 0; + + address.ss_len = 0; + peer.ss_len = 0; + + mutex_init(&lock, "socket"); + + // set defaults (may be overridden by the protocols) + send.buffer_size = 65535; + send.low_water_mark = 1; + send.timeout = B_INFINITE_TIMEOUT; + receive.buffer_size = 65535; + receive.low_water_mark = 1; + receive.timeout = B_INFINITE_TIMEOUT; +} + + +net_socket_private::~net_socket_private() +{ + if (parent != NULL) + panic("socket still has a parent!"); + + mutex_lock(&sSocketLock); + sSocketList.Remove(this); + mutex_unlock(&sSocketLock); + + mutex_lock(&lock); + + // also delete all children of this socket + while (net_socket_private* child = pending_children.RemoveHead()) { + child->RemoveFromParent(); + } + while (net_socket_private* child = connected_children.RemoveHead()) { + child->RemoveFromParent(); + } + + put_domain_protocols(this); + + mutex_unlock(&lock); + mutex_destroy(&lock); +} + + +void +net_socket_private::RemoveFromParent() +{ + parent->RemoveReference(); + parent = NULL; + + mutex_lock(&sSocketLock); + sSocketList.Add(this); + mutex_unlock(&sSocketLock); + + RemoveReference(); +} + + +// #pragma mark - + + static size_t compute_user_iovec_length(iovec* userVec, uint32 count) { @@ -84,16 +166,6 @@ compute_user_iovec_length(iovec* userVec, uint32 count) } -static void -delete_children(SocketList& list) -{ - while (net_socket_private* child = list.RemoveHead()) { - child->parent = NULL; - socket_delete(child); - } -} - - static status_t create_socket(int family, int type, int protocol, net_socket_private** _socket) { @@ -101,21 +173,10 @@ create_socket(int family, int type, int protocol, net_socket_private** _socket) if (socket == NULL) return B_NO_MEMORY; - memset(socket, 0, sizeof(net_socket_private)); socket->family = family; socket->type = type; socket->protocol = protocol; - mutex_init(&socket->lock, "socket"); - - // set defaults (may be overridden by the protocols) - socket->send.buffer_size = 65535; - socket->send.low_water_mark = 1; - socket->send.timeout = B_INFINITE_TIMEOUT; - socket->receive.buffer_size = 65535; - socket->receive.low_water_mark = 1; - socket->receive.timeout = B_INFINITE_TIMEOUT; - status_t status = get_domain_protocols(socket); if (status < B_OK) { mutex_destroy(&socket->lock); @@ -316,7 +377,7 @@ socket_open(int family, int type, int protocol, net_socket** _socket) status = socket->first_info->open(socket->first_protocol); if (status < B_OK) { - socket_delete(socket); + delete socket; return status; } @@ -339,15 +400,12 @@ socket_close(net_socket* _socket) } -status_t -socket_free(net_socket* socket) +void +socket_free(net_socket* _socket) { - status_t status = socket->first_info->free(socket->first_protocol); - if (status == B_BUSY) - return B_OK; - - socket_delete(socket); - return B_OK; + net_socket_private* socket = (net_socket_private*)_socket; + socket->first_info->free(socket->first_protocol); + socket->RemoveReference(); } @@ -532,6 +590,22 @@ socket_get_next_stat(uint32* _cookie, int family, struct net_stat* stat) // #pragma mark - connections +void +socket_acquire(net_socket* _socket) +{ + net_socket_private* socket = (net_socket_private*)_socket; + socket->AddReference(); +} + + +bool +socket_release(net_socket* _socket) +{ + net_socket_private* socket = (net_socket_private*)_socket; + return socket->RemoveReference(); +} + + status_t socket_spawn_pending(net_socket* _parent, net_socket** _socket) { @@ -562,7 +636,7 @@ socket_spawn_pending(net_socket* _parent, net_socket** _socket) // add to the parent's list of pending connections parent->pending_children.Add(socket); - socket->parent = parent; + socket->parent = parent->GetWeakPointer(); parent->child_count++; *_socket = socket; @@ -570,42 +644,9 @@ socket_spawn_pending(net_socket* _parent, net_socket** _socket) } -void -socket_delete(net_socket* _socket) -{ - net_socket_private* socket = (net_socket_private*)_socket; - net_socket_private* parent = (net_socket_private*)socket->parent; - - if (parent != NULL) { - // The socket still has a parent - // TODO: we need to make sure our parent isn't deleted right now, - // or in the process of deleting its children... - MutexLocker _(parent->lock); - - if (socket->is_connected) - parent->connected_children.Remove(socket); - else - parent->pending_children.Remove(socket); - - parent->child_count--; - socket->parent = NULL; - } - - mutex_lock(&sSocketLock); - sSocketList.Remove(socket); - mutex_unlock(&sSocketLock); - - // also delete all children of this socket - delete_children(socket->pending_children); - delete_children(socket->connected_children); - - put_domain_protocols(socket); - - mutex_destroy(&socket->lock); - delete socket; -} - - +/*! Dequeues a connected child from a parent socket. + It also returns a reference with the child socket. +*/ status_t socket_dequeue_connected(net_socket* _parent, net_socket** _socket) { @@ -615,7 +656,8 @@ socket_dequeue_connected(net_socket* _parent, net_socket** _socket) net_socket_private* socket = parent->connected_children.RemoveHead(); if (socket != NULL) { - socket->parent = NULL; + socket->AddReference(); + socket->RemoveFromParent(); parent->child_count--; *_socket = socket; } @@ -625,10 +667,6 @@ socket_dequeue_connected(net_socket* _parent, net_socket** _socket) if (socket == NULL) return B_ENTRY_NOT_FOUND; - mutex_lock(&sSocketLock); - sSocketList.Add(socket); - mutex_unlock(&sSocketLock); - return B_OK; } @@ -659,13 +697,12 @@ socket_set_max_backlog(net_socket* _socket, uint32 backlog) net_socket_private* child; while (socket->child_count > backlog && (child = socket->pending_children.RemoveTail()) != NULL) { - child->parent = NULL; + child->RemoveFromParent(); socket->child_count--; } while (socket->child_count > backlog && (child = socket->connected_children.RemoveTail()) != NULL) { - child->parent = NULL; - socket_delete(child); + child->RemoveFromParent(); socket->child_count--; } @@ -674,21 +711,34 @@ socket_set_max_backlog(net_socket* _socket, uint32 backlog) } +/*! Returns whether or not this socket has a parent. The parent might not be + valid anymore, though. +*/ +bool +socket_has_parent(net_socket* _socket) +{ + net_socket_private* socket = (net_socket_private*)_socket; + return socket->parent != NULL; +} + + /*! The socket has been connected. It will be moved to the connected queue of its parent socket. */ status_t -socket_connected(net_socket* socket) +socket_connected(net_socket* _socket) { - net_socket_private* parent = (net_socket_private*)socket->parent; + net_socket_private* socket = (net_socket_private*)_socket; + + WeakReference parent = socket->parent; if (parent == NULL) return B_BAD_VALUE; - MutexLocker _(&parent->lock); + MutexLocker _(parent->lock); - parent->pending_children.Remove((net_socket_private*)socket); - parent->connected_children.Add((net_socket_private*)socket); - ((net_socket_private*)socket)->is_connected = true; + parent->pending_children.Remove(socket); + parent->connected_children.Add(socket); + socket->is_connected = true; // notify parent if (parent->select_pool) @@ -698,6 +748,32 @@ socket_connected(net_socket* socket) } +/*! The socket has been aborted. Steals the parent's reference, and releases + it. +*/ +status_t +socket_aborted(net_socket* _socket) +{ + net_socket_private* socket = (net_socket_private*)_socket; + + WeakReference parent = socket->parent; + if (parent == NULL) + return B_BAD_VALUE; + + MutexLocker _(parent->lock); + + if (socket->is_connected) + parent->connected_children.Remove(socket); + else + parent->pending_children.Remove(socket); + + parent->child_count--; + socket->RemoveFromParent(); + + return B_OK; +} + + // #pragma mark - notifications @@ -1551,12 +1627,15 @@ net_socket_module_info gNetSocketModule = { socket_get_next_stat, // connections + socket_acquire, + socket_release, socket_spawn_pending, - socket_delete, socket_dequeue_connected, socket_count_connected, socket_set_max_backlog, + socket_has_parent, socket_connected, + socket_aborted, // notifications socket_request_notification, diff --git a/src/add-ons/kernel/network/stack/stack_interface.cpp b/src/add-ons/kernel/network/stack/stack_interface.cpp index 20e23567d3..f53e4b7004 100644 --- a/src/add-ons/kernel/network/stack/stack_interface.cpp +++ b/src/add-ons/kernel/network/stack/stack_interface.cpp @@ -27,7 +27,8 @@ stack_interface_close(net_socket* socket) static status_t stack_interface_free(net_socket* socket) { - return gNetSocketModule.free(socket); + gNetSocketModule.free(socket); + return B_OK; }