From 6ff565b0d6b14be5e40532fc862b23553d9941d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 19 Nov 2006 23:35:07 +0000 Subject: [PATCH] * Moved the private kernel locking functions into kernelland_emu.cpp because the actual versions cannot work in userland. * Extended the tcp_tester to be able to connect and quit connections. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19330 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tests/add-ons/kernel/Jamfile | 5 - src/tests/add-ons/kernel/kernelland_emu.cpp | 240 ++++++++++++++++- src/tests/kits/net/tcp_tester.cpp | 283 +++++++++++++++++--- 3 files changed, 480 insertions(+), 48 deletions(-) diff --git a/src/tests/add-ons/kernel/Jamfile b/src/tests/add-ons/kernel/Jamfile index 21cfeb1469..02f6e5da53 100644 --- a/src/tests/add-ons/kernel/Jamfile +++ b/src/tests/add-ons/kernel/Jamfile @@ -8,7 +8,6 @@ SharedLibrary libkernelland_emu.so : kernelland_emu.cpp strlcpy.c khash.c - lock.c list.c : be stdc++.r4 ; @@ -20,10 +19,6 @@ SEARCH on [ FGristFiles strlcpy.c ] = [ FDirName $(HAIKU_TOP) src system libroot posix string ] ; -SEARCH on [ FGristFiles - lock.c - ] = [ FDirName $(HAIKU_TOP) src system kernel ] ; - SEARCH on [ FGristFiles list.c khash.c ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; diff --git a/src/tests/add-ons/kernel/kernelland_emu.cpp b/src/tests/add-ons/kernel/kernelland_emu.cpp index 343dfc5508..e0a068675d 100644 --- a/src/tests/add-ons/kernel/kernelland_emu.cpp +++ b/src/tests/add-ons/kernel/kernelland_emu.cpp @@ -1,8 +1,7 @@ // kernelland_emu.cpp -#include -#include -#include +#include +#include #include #include @@ -15,7 +14,9 @@ #include #include -#include +#include +#include +#include #ifdef TRACE #undef TRACE @@ -786,3 +787,234 @@ arch_int_are_interrupts_enabled(void) return true; } + +// #pragma mark - Private locking functions + + +int +recursive_lock_get_recursion(recursive_lock *lock) +{ + thread_id thid = find_thread(NULL); + + if (lock->holder == thid) + return lock->recursion; + + return -1; +} + + +status_t +recursive_lock_init(recursive_lock *lock, const char *name) +{ + if (lock == NULL) + return B_BAD_VALUE; + + if (name == NULL) + name = "recursive lock"; + + lock->holder = -1; + lock->recursion = 0; + lock->sem = create_sem(1, name); + + if (lock->sem >= B_OK) + return B_OK; + + return lock->sem; +} + + +void +recursive_lock_destroy(recursive_lock *lock) +{ + if (lock == NULL) + return; + + delete_sem(lock->sem); + lock->sem = -1; +} + + +bool +recursive_lock_lock(recursive_lock *lock) +{ + thread_id thid = find_thread(NULL); + bool retval = false; + + if (thid != lock->holder) { + acquire_sem(lock->sem); + + lock->holder = thid; + retval = true; + } + lock->recursion++; + return retval; +} + + +bool +recursive_lock_unlock(recursive_lock *lock) +{ + thread_id thid = find_thread(NULL); + bool retval = false; + + if (thid != lock->holder) + panic("recursive_lock %p unlocked by non-holder thread!\n", lock); + + if (--lock->recursion == 0) { + lock->holder = -1; + release_sem(lock->sem); + retval = true; + } + return retval; +} + + +// #pragma mark - + + +status_t +mutex_init(mutex *m, const char *name) +{ + if (m == NULL) + return EINVAL; + + if (name == NULL) + name = "mutex_sem"; + + m->holder = -1; + + m->sem = create_sem(1, name); + if (m->sem >= B_OK) + return B_OK; + + return m->sem; +} + + +void +mutex_destroy(mutex *mutex) +{ + if (mutex == NULL) + return; + + if (mutex->sem >= 0) { + delete_sem(mutex->sem); + mutex->sem = -1; + } + mutex->holder = -1; +} + + +void +mutex_lock(mutex *mutex) +{ + thread_id me = find_thread(NULL); + + // ToDo: if acquire_sem() fails, we shouldn't panic - but we should definitely + // change the mutex API to actually return the status code + if (acquire_sem(mutex->sem) == B_OK) { + if (me == mutex->holder) + panic("mutex_lock failure: mutex %p (sem = 0x%lx) acquired twice by thread 0x%lx\n", mutex, mutex->sem, me); + } + + mutex->holder = me; +} + + +void +mutex_unlock(mutex *mutex) +{ + thread_id me = find_thread(NULL); + + if (me != mutex->holder) + panic("mutex_unlock failure: thread 0x%lx is trying to release mutex %p (current holder 0x%lx)\n", + me, mutex, mutex->holder); + + mutex->holder = -1; + release_sem(mutex->sem); +} + + +// #pragma mark - + + +status_t +benaphore_init(benaphore *ben, const char *name) +{ + if (ben == NULL || name == NULL) + return B_BAD_VALUE; + + ben->count = 1; + ben->sem = create_sem(0, name); + if (ben->sem >= B_OK) + return B_OK; + + return ben->sem; +} + + +void +benaphore_destroy(benaphore *ben) +{ + delete_sem(ben->sem); + ben->sem = -1; +} + + +// #pragma mark - + + +status_t +rw_lock_init(rw_lock *lock, const char *name) +{ + if (lock == NULL) + return B_BAD_VALUE; + + if (name == NULL) + name = "r/w lock"; + + lock->sem = create_sem(RW_MAX_READERS, name); + if (lock->sem >= B_OK) + return B_OK; + + return lock->sem; +} + + +void +rw_lock_destroy(rw_lock *lock) +{ + if (lock == NULL) + return; + + delete_sem(lock->sem); +} + + +status_t +rw_lock_read_lock(rw_lock *lock) +{ + return acquire_sem(lock->sem); +} + + +status_t +rw_lock_read_unlock(rw_lock *lock) +{ + return release_sem(lock->sem); +} + + +status_t +rw_lock_write_lock(rw_lock *lock) +{ + return acquire_sem_etc(lock->sem, RW_MAX_READERS, 0, 0); +} + + +status_t +rw_lock_write_unlock(rw_lock *lock) +{ + return release_sem_etc(lock->sem, RW_MAX_READERS, 0); +} + diff --git a/src/tests/kits/net/tcp_tester.cpp b/src/tests/kits/net/tcp_tester.cpp index 3ea5a4047b..bd8015411d 100644 --- a/src/tests/kits/net/tcp_tester.cpp +++ b/src/tests/kits/net/tcp_tester.cpp @@ -9,8 +9,10 @@ #include #include +#include #include +#include #include #include #include @@ -36,9 +38,9 @@ extern module_info *modules[]; extern struct net_protocol_module_info gDomainModule; static struct net_protocol sDomainProtocol; struct net_interface gInterface; -struct net_socket_module_info gNetSocketModule; +extern struct net_socket_module_info gNetSocketModule; struct net_protocol_module_info *gTCPModule; -struct net_socket gServerSocket, gClientSocket; +struct net_socket *gServerSocket, *gClientSocket; static struct net_domain sDomain = { "ipv4", @@ -128,54 +130,68 @@ static net_stack_module_info gNetStackModule = { }; -// #pragma mark - protocol/socket +// #pragma mark - socket -net_protocol* -init_protocol(net_socket& socket) +status_t +socket_create(int family, int type, int protocol, net_socket **_socket) { - memset(&socket, 0, sizeof(net_socket)); - socket.family = AF_INET; - socket.type = SOCK_STREAM; - socket.protocol = IPPROTO_TCP; + struct net_socket *socket = new (std::nothrow) net_socket; + if (socket == NULL) + return B_NO_MEMORY; + + memset(socket, 0, sizeof(net_socket)); + socket->family = family; + socket->type = type; + socket->protocol = protocol; + + status_t status = benaphore_init(&socket->lock, "socket"); + if (status < B_OK) + goto err1; // set defaults (may be overridden by the protocols) - socket.send.buffer_size = 65536; - socket.send.low_water_mark = 1; - socket.send.timeout = B_INFINITE_TIMEOUT; - socket.receive.buffer_size = 65536; - socket.receive.low_water_mark = 1; - socket.receive.timeout = B_INFINITE_TIMEOUT; + socket->send.buffer_size = 65536; + socket->send.low_water_mark = 1; + socket->send.timeout = B_INFINITE_TIMEOUT; + socket->receive.buffer_size = 65536; + socket->receive.low_water_mark = 1; + socket->receive.timeout = B_INFINITE_TIMEOUT; - net_protocol* protocol = gTCPModule->init_protocol(&socket); - if (protocol == NULL) { + list_init_etc(&socket->pending_children, offsetof(net_socket, link)); + list_init_etc(&socket->connected_children, offsetof(net_socket, link)); + + socket->first_protocol = gTCPModule->init_protocol(socket); + if (socket->first_protocol == NULL) { fprintf(stderr, "tcp_tester: cannot create protocol\n"); - return NULL; + goto err2; } - socket.first_info = gTCPModule; - socket.first_protocol = protocol; + socket->first_info = gTCPModule; - protocol->next = &sDomainProtocol; - protocol->module = gTCPModule; - protocol->socket = &socket; + socket->first_protocol->next = &sDomainProtocol; + socket->first_protocol->module = gTCPModule; + socket->first_protocol->socket = socket; - status_t status = gTCPModule->open(protocol); - if (status < B_OK) { - fprintf(stderr, "tcp_tester: cannot open client: %s\n", strerror(status)); - return NULL; - } + *_socket = socket; + return B_OK; - return protocol; +err2: + benaphore_destroy(&socket->lock); +err1: + delete socket; + return status; } void -close_protocol(net_protocol* protocol) +socket_delete(net_socket *socket) { - gTCPModule->close(protocol); - gTCPModule->free(protocol); - gTCPModule->uninit_protocol(protocol); + if (socket->parent != NULL) + panic("socket still has a parent!"); + + socket->first_info->uninit_protocol(socket->first_protocol); + benaphore_destroy(&socket->lock); + delete socket; } @@ -261,6 +277,187 @@ socket_listen(net_socket *socket, int backlog) } +status_t +socket_spawn_pending(net_socket *parent, net_socket **_socket) +{ + BenaphoreLocker locker(parent->lock); + + // We actually accept more pending connections to compensate for those + // that never complete, and also make sure at least a single connection + // can always be accepted + if (parent->child_count > 3 * parent->max_backlog / 2) + return ENOBUFS; + + net_socket *socket; + status_t status = socket_create(parent->family, parent->type, parent->protocol, &socket); + if (status < B_OK) + return status; + + // inherit parent's properties + socket->send = parent->send; + socket->receive = parent->receive; + socket->options = parent->options & ~SO_ACCEPTCONN; + socket->linger = parent->linger; + memcpy(&socket->address, &parent->address, parent->address.ss_len); + memcpy(&socket->peer, &parent->peer, parent->peer.ss_len); + + // add to the parent's list of pending connections + list_add_item(&parent->pending_children, socket); + parent->child_count++; + + *_socket = socket; + return B_OK; +} + + +status_t +socket_dequeue_connected(net_socket *parent, net_socket **_socket) +{ + benaphore_lock(&parent->lock); + + net_socket *socket = (net_socket *)list_remove_head_item(&parent->connected_children); + if (socket != NULL) { + socket->parent = NULL; + parent->child_count--; + *_socket = socket; + } + + benaphore_unlock(&parent->lock); + return socket != NULL ? B_OK : B_ENTRY_NOT_FOUND; +} + + +status_t +socket_set_max_backlog(net_socket *socket, uint32 backlog) +{ + // we enforce an upper limit of connections waiting to be accepted + if (backlog > 256) + backlog = 256; + + benaphore_lock(&socket->lock); + + // first remove the pending connections, then the already connected ones as needed + net_socket *child; + while (socket->child_count > backlog + && (child = (net_socket *)list_remove_tail_item(&socket->pending_children)) != NULL) { + child->parent = NULL; + socket->child_count--; + } + while (socket->child_count > backlog + && (child = (net_socket *)list_remove_tail_item(&socket->connected_children)) != NULL) { + child->parent = NULL; + socket_delete(child); + socket->child_count--; + } + + socket->max_backlog = backlog; + benaphore_unlock(&socket->lock); + return B_OK; +} + + +/*! + The socket has been connected. It will be moved to the connected queue + of its parent socket. +*/ +status_t +socket_connected(net_socket *socket) +{ + net_socket *parent = socket->parent; + if (parent == NULL) + return B_BAD_VALUE; + + benaphore_lock(&parent->lock); + + list_remove_item(&parent->pending_children, socket); + list_add_item(&parent->connected_children, socket); + + benaphore_unlock(&parent->lock); + return B_OK; +} + + +net_socket_module_info gNetSocketModule = { + { + NET_SOCKET_MODULE_NAME, + 0, + std_ops + }, + NULL, //socket_open, + NULL, //socket_close, + NULL, //socket_free, + + NULL, //socket_readv, + NULL, //socket_writev, + NULL, //socket_control, + + NULL, //socket_read_avail, + NULL, //socket_send_avail, + + NULL, //socket_send_data, + NULL, //socket_receive_data, + + // connections + socket_spawn_pending, + socket_delete, + socket_dequeue_connected, + socket_set_max_backlog, + socket_connected, + + // notifications + NULL, //socket_request_notification, + NULL, //socket_cancel_notification, + NULL, //socket_notify, + + // standard socket API + NULL, //socket_accept, + NULL, //socket_bind, + NULL, //socket_connect, + NULL, //socket_getpeername, + NULL, //socket_getsockname, + NULL, //socket_getsockopt, + NULL, //socket_listen, + NULL, //socket_recv, + NULL, //socket_recvfrom, + NULL, //socket_send, + NULL, //socket_sendto, + NULL, //socket_setsockopt, + NULL, //socket_shutdown, +}; + + +// #pragma mark - protocol + + +net_protocol* +init_protocol(net_socket** _socket) +{ + net_socket *socket; + status_t status = socket_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &socket); + if (status < B_OK) + return NULL; + + status = socket->first_info->open(socket->first_protocol); + if (status < B_OK) { + fprintf(stderr, "tcp_tester: cannot open client: %s\n", strerror(status)); + socket_delete(socket); + return NULL; + } + + *_socket = socket; + return socket->first_protocol; +} + + +void +close_protocol(net_protocol* protocol) +{ + gTCPModule->close(protocol); + gTCPModule->free(protocol); + gTCPModule->uninit_protocol(protocol); +} + + // #pragma mark - datalink @@ -493,7 +690,7 @@ server_thread(void *) net_socket* connectionSocket; sockaddr_in address; uint32 size = sizeof(struct sockaddr_in); - status_t status = socket_accept(&gServerSocket, (struct sockaddr *)&address, + status_t status = socket_accept(gServerSocket, (struct sockaddr *)&address, &size, &connectionSocket); if (status < B_OK) { fprintf(stderr, "SERVER: accepting failed: %s\n", strerror(status)); @@ -523,7 +720,7 @@ do_connect(int argc, char** argv) address.sin_port = htons(port); address.sin_addr.s_addr = INADDR_ANY; - status_t status = socket_connect(&gClientSocket, (struct sockaddr *)&address, + status_t status = socket_connect(gClientSocket, (struct sockaddr *)&address, sizeof(struct sockaddr)); if (status < B_OK) fprintf(stderr, "tcp_tester: could not connect: %s\n", strerror(status)); @@ -561,6 +758,7 @@ main(int argc, char** argv) _add_builtin_module((module_info *)&gNetStackModule); _add_builtin_module((module_info *)&gNetBufferModule); + _add_builtin_module((module_info *)&gNetSocketModule); _add_builtin_module((module_info *)&gNetDatalinkModule); _add_builtin_module(modules[0]); @@ -568,6 +766,7 @@ main(int argc, char** argv) sockaddr_in interfaceAddress; interfaceAddress.sin_len = sizeof(sockaddr_in); interfaceAddress.sin_family = AF_INET; + interfaceAddress.sin_addr.s_addr = htonl(0xc0a80001); gInterface.address = (sockaddr*)&interfaceAddress; status = get_module("network/protocols/tcp/v1", (module_info **)&gTCPModule); @@ -577,10 +776,10 @@ main(int argc, char** argv) return 1; } - net_protocol* client = init_protocol(gClientSocket); + net_protocol* client = init_protocol(&gClientSocket); if (client == NULL) return 1; - net_protocol* server = init_protocol(gServerSocket); + net_protocol* server = init_protocol(&gServerSocket); if (server == NULL) return 1; @@ -594,12 +793,12 @@ main(int argc, char** argv) address.sin_port = htons(1024); address.sin_addr.s_addr = INADDR_ANY; - status = socket_bind(&gServerSocket, (struct sockaddr *)&address, sizeof(struct sockaddr)); + status = socket_bind(gServerSocket, (struct sockaddr *)&address, sizeof(struct sockaddr)); if (status < B_OK) { fprintf(stderr, "tcp_tester: cannot bind server: %s\n", strerror(status)); return 1; } - status = socket_listen(&gServerSocket, 40); + status = socket_listen(gServerSocket, 40); if (status < B_OK) { fprintf(stderr, "tcp_tester: server cannot listen: %s\n", strerror(status)); return 1; @@ -639,13 +838,19 @@ main(int argc, char** argv) || !strcmp(argv[0], "q")) break; + bool found = false; + for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { if (!strncmp(command->name, argv[0], length)) { command->func(argc, argv); + found = true; break; } } + if (!found) + fprintf(stderr, "Unknown command \"%s\". Type \"help\" for a list of commands.\n", argv[0]); + free(argv); }