From bb039bc91ef7d5b82cdba64e0a8a965790e06700 Mon Sep 17 00:00:00 2001 From: Michael Graff Date: Tue, 10 Nov 1998 01:56:44 +0000 Subject: [PATCH] snapshot --- bin/tests/sock_test.c | 14 +- lib/isc/include/isc/socket.h | 7 +- lib/isc/unix/socket.c | 284 +++++++++++++++++++++++++++-------- 3 files changed, 237 insertions(+), 68 deletions(-) diff --git a/bin/tests/sock_test.c b/bin/tests/sock_test.c index 916ee9f3a8..69171b80dd 100644 --- a/bin/tests/sock_test.c +++ b/bin/tests/sock_test.c @@ -37,8 +37,6 @@ my_shutdown(isc_task_t task, isc_event_t event) fflush(stdout); isc_event_free(&event); - tasks_done++; - return (ISC_TRUE); } @@ -51,6 +49,8 @@ my_listen(isc_task_t task, isc_event_t event) fflush(stdout); isc_event_free(&event); + tasks_done++; + return (ISC_TRUE); } @@ -138,11 +138,6 @@ main(int argc, char *argv[]) sizeof *event); isc_task_send(t2, &event); - isc_task_shutdown(t1); - isc_task_shutdown(t2); - isc_task_detach(&t1); - isc_task_detach(&t2); - /* * Grr! there is no way to say "wake me when it's over" */ @@ -151,6 +146,11 @@ main(int argc, char *argv[]) sleep(2); } + isc_task_shutdown(t1); + isc_task_shutdown(t2); + isc_task_detach(&t1); + isc_task_detach(&t2); + printf("destroy\n"); isc_socket_detach(&so1); isc_socket_detach(&so2); diff --git a/lib/isc/include/isc/socket.h b/lib/isc/include/isc/socket.h index 94acff7e68..bc8eafac21 100644 --- a/lib/isc/include/isc/socket.h +++ b/lib/isc/include/isc/socket.h @@ -1,4 +1,4 @@ -/* $Id: socket.h,v 1.2 1998/11/06 01:45:35 explorer Exp $ */ +/* $Id: socket.h,v 1.3 1998/11/10 01:56:44 explorer Exp $ */ #ifndef ISC_SOCKET_H #define ISC_SOCKET_H 1 @@ -82,6 +82,11 @@ typedef struct isc_socketevent { int addrlength; /* length of address */ } *isc_socketevent_t; +typedef struct isc_socket_newconev { + struct isc_event common; + isc_socket_t newsocket; +} *isc_socket_newconnev_t; + #define ISC_SOCKEVENT_ANYEVENT (0) #define ISC_SOCKEVENT_RECVDONE (ISC_EVENTCLASS_SOCKET + 1) #define ISC_SOCKEVENT_SENDDONE (ISC_EVENTCLASS_SOCKET + 2) diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index 1048b25756..a19df9b2d2 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -1,4 +1,4 @@ -/* $Id: socket.c,v 1.3 1998/11/07 02:31:04 explorer Exp $ */ +/* $Id: socket.c,v 1.4 1998/11/10 01:56:43 explorer Exp $ */ #include "attribute.h" @@ -40,9 +40,9 @@ * Debugging */ #if 1 -#define XTRACE(a) fprintf(stderr, a) -#define XENTER(a) fprintf(stderr, "ENTER %s\n", (a)) -#define XEXIT(a) fprintf(stderr, "EXIT %s\n", (a)) +#define XTRACE(a) printf a +#define XENTER(a) printf("ENTER %s\n", (a)) +#define XEXIT(a) printf("EXIT %s\n", (a)) #else #define XTRACE(a) #define XENTER(a) @@ -58,12 +58,20 @@ typedef struct isc_socket_intev { isc_task_t task; /* task to send these to */ isc_socketevent_t done_ev; /* the done event to post */ isc_boolean_t partial; /* partial i/o ok */ - isc_boolean_t listener; + isc_boolean_t canceled; /* I/O was canceled */ isc_taskaction_t action; /* listen needs this too */ void *arg; /* listen needs this too */ LINK(struct isc_socket_intev) link; } *isc_socket_intev_t; +typedef struct isc_socket_ncintev { + struct isc_event common; + isc_boolean_t canceled; + isc_task_t task; + isc_socket_newconnev_t done; /* the done event */ + isc_socket_t sock; /* the socket we will pass or destroy */ +} *isc_socket_ncintev_t; + #define SOCKET_MAGIC 0x494f696fU /* IOio */ #define VALID_SOCKET(t) ((t) != NULL && \ (t)->magic == SOCKET_MAGIC) @@ -79,6 +87,7 @@ struct isc_socket { LIST(struct isc_socket_intev) write_list; isc_boolean_t pending_read; isc_boolean_t pending_write; + isc_boolean_t listener; /* listener socket */ isc_sockettype_t type; isc_socket_intev_t riev; isc_socket_intev_t wiev; @@ -106,42 +115,45 @@ struct isc_socketmgr { sig_atomic_t pipe_msgs; }; -typedef int select_msg_t; #define SELECT_POKE_SHUTDOWN (-1) #define SELECT_POKE_NOTHING (-2) static void send_done_event(isc_socket_t, isc_socket_intev_t *, isc_socketevent_t *, isc_result_t); static void done_event_destroy(isc_event_t); +static void free_socket(isc_socket_t *); +static isc_result_t allocate_socket(isc_socketmgr_t, isc_sockettype_t, + isc_socket_t *); /* * poke the select loop when there is something for us to do. Manager must * be locked. */ static void -select_poke(isc_socketmgr_t mgr, select_msg_t msg) +select_poke(isc_socketmgr_t mgr, int msg) { int cc; - cc = write(mgr->pipe_fds[1], &msg, sizeof(select_msg_t)); + XTRACE(("Poking watcher with data %d\n", msg)); + cc = write(mgr->pipe_fds[1], &msg, sizeof(int)); if (cc < 0) UNEXPECTED_ERROR(__FILE__, __LINE__, "write() failed during watcher poke: %s", strerror(errno)); - INSIST(cc == sizeof(select_msg_t)); + INSIST(cc == sizeof(int)); } /* * read a message on the internal fd. */ -static select_msg_t +static int select_readmsg(isc_socketmgr_t mgr) { - select_msg_t msg; + int msg; int cc; - cc = read(mgr->pipe_fds[0], &msg, sizeof(select_msg_t)); + cc = read(mgr->pipe_fds[0], &msg, sizeof(int)); if (cc < 0) { if (errno == EWOULDBLOCK) return SELECT_POKE_NOTHING; @@ -152,7 +164,7 @@ select_readmsg(isc_socketmgr_t mgr) return SELECT_POKE_NOTHING; /* XXX */ } - INSIST(cc == sizeof(select_msg_t)); + INSIST(cc == sizeof(int)); return msg; } @@ -211,21 +223,87 @@ destroy(isc_socket_t sock) /* * Noone has this socket open, so the watcher doesn't have to be - * poked. + * poked, and the socket doesn't have to be locked. */ manager->fds[sock->fd] = NULL; manager->nsockets--; UNLOCK(&manager->lock); - if (sock->riev) - isc_event_free((isc_event_t *)&sock->riev); - if (sock->wiev) - isc_event_free((isc_event_t *)&sock->wiev); + free_socket(&sock); +} + +static isc_result_t +allocate_socket(isc_socketmgr_t manager, isc_sockettype_t type, + isc_socket_t *socketp) +{ + isc_socket_t sock; + + sock = isc_mem_get(manager->mctx, sizeof *sock); + + if (sock == NULL) + return (NULL); + + sock->magic = SOCKET_MAGIC; + sock->references = 1; + + sock->manager = manager; + sock->type = type; + + /* + * set up list of readers and writers to be initially empty + */ + INIT_LIST(sock->read_list); + INIT_LIST(sock->write_list); + sock->pending_read = ISC_FALSE; + sock->pending_write = ISC_FALSE; + + /* + * initialize the lock + */ + if (isc_mutex_init(&sock->lock) != ISC_R_SUCCESS) { + sock->magic = 0; + isc_mem_put(manager->mctx, sock, sizeof *sock); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed"); + return (ISC_R_UNEXPECTED); + } + + *socketp = sock; + + return (ISC_R_SUCCESS); +} + +/* + * This event requires that the various lists be empty, that the reference + * count be 1, and that the magic number is valid. The other socket bits, + * like the lock, must be initialized as well. The fd associated must be + * marked as closed, by setting it to -1 on close, or this routine will + * also close the socket. + */ +static void +free_socket(isc_socket_t *socketp) +{ + isc_socket_t sock = *socketp; + + REQUIRE(sock->references == 1); + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(!sock->listener); + REQUIRE(!sock->pending_read); + REQUIRE(!sock->pending_write); + REQUIRE(EMPTY(sock->read_list)); + REQUIRE(EMPTY(sock->write_list)); + + sock->magic = 0; + + if (sock->fd == -1) { + close(sock->fd); + sock->fd = -1; + } (void)isc_mutex_destroy(&sock->lock); - sock->magic = 0; - isc_mem_put(manager->mctx, sock, sizeof *sock); + + isc_mem_put(sock->manager->mctx, sock, sizeof *sock); } /* @@ -239,29 +317,18 @@ isc_result_t isc_socket_create(isc_socketmgr_t manager, isc_sockettype_t type, isc_socket_t *socketp) { - isc_socket_t sock; + isc_socket_t sock = NULL; + isc_result_t ret; REQUIRE(VALID_MANAGER(manager)); REQUIRE(socketp != NULL && *socketp == NULL); XENTER("isc_socket_create"); - sock = isc_mem_get(manager->mctx, sizeof *sock); - if (sock == NULL) - return (ISC_R_NOMEMORY); - - sock->magic = SOCKET_MAGIC; - sock->manager = manager; - sock->references = 1; - sock->type = type; - - /* - * set up list of readers and writers to be initially empty - */ - INIT_LIST(sock->read_list); - INIT_LIST(sock->write_list); - sock->pending_read = ISC_FALSE; - sock->pending_write = ISC_FALSE; + + ret = allocate_socket(manager, type, &sock); + if (ret != ISC_R_SUCCESS) + return (ret); /* * Create the associated socket XXX @@ -275,7 +342,7 @@ isc_socket_create(isc_socketmgr_t manager, isc_sockettype_t type, break; } if (sock->fd < 0) { - isc_mem_put(manager->mctx, sock, sizeof *sock); + free_socket(&sock); switch (errno) { case EMFILE: @@ -293,24 +360,12 @@ isc_socket_create(isc_socketmgr_t manager, isc_sockettype_t type, } if (make_nonblock(sock->fd) != ISC_R_SUCCESS) { - isc_mem_put(manager->mctx, sock, sizeof *sock); - close(sock->fd); + free_socket(&sock); UNEXPECTED_ERROR(__FILE__, __LINE__, "make_nonblock(%d)", sock->fd); return (ISC_R_UNEXPECTED); } - /* - * initialize the lock - */ - if (isc_mutex_init(&sock->lock) != ISC_R_SUCCESS) { - isc_mem_put(manager->mctx, sock, sizeof *sock); - close(sock->fd); - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_mutex_init() failed"); - return (ISC_R_UNEXPECTED); - } - LOCK(&manager->lock); /* @@ -440,9 +495,76 @@ send_done_event(isc_socket_t sock, isc_socket_intev_t *iev, isc_event_free((isc_event_t *)iev); } +/* + * Call accept() on a socket, to get the new file descriptor. The listen + * socket is used as a prototype to create a new isc_socket_t. The new + * socket is referenced twice (one for the task which is receiving this + * message, and once for the message itself) so the task does not need to + * attach to the socket again. The task is not attached at all. + */ static isc_boolean_t internal_accept(isc_task_t task, isc_event_t ev) { + isc_socket_t sock; + isc_socket_t nsock = NULL; + isc_socket_newconnev_t dev; + isc_socket_ncintev_t iev; + struct sockaddr addr; + int addrlen; + int fd; + + sock = ev->sender; + iev = (isc_socket_ncintev_t)ev; + + LOCK(&sock->lock); + XTRACE(("internal_accept called, locked sock %p\n", sock)); + + REQUIRE(sock->pending_read); + REQUIRE(sock->listener); + REQUIRE(!EMPTY(sock->read_list)); + REQUIRE(iev->task == task); + + sock->pending_read = ISC_FALSE; + + /* + * Try to accept the new connection. If the accept fails with + * EWOULDBLOCK, simply poke the watcher to watch this socket + * again. + */ + fd = accept(sock->fd, &addr, &addrlen); + if (fd < 0) { + if (errno == EWOULDBLOCK) { + sock->pending_read = ISC_FALSE; + select_poke(sock->manager, sock->fd); + UNLOCK(&sock->lock); + return (0); + } + + /* + * If some other error, ignore it as well and hope + * for the best, but log it. XXX This will have to be + * changed, thanks to broken OSs trying to overload what + * accept does. + */ + sock->pending_read = ISC_FALSE; + select_poke(sock->manager, sock->fd); + UNLOCK(&sock->lock); + return (0); + } + + /* + * The accept succeeded. Pull off the done event and set the + * fd and other information in the socket descriptor here. These + * were preallocated for us. + */ + dev = iev->done; + nsock = iev->sock; + + nsock->fd = fd; + + dev->newsocket = nsock; + + return (0); } static isc_boolean_t @@ -596,7 +718,7 @@ watcher(void *uap) int cc; fd_set readfds; fd_set writefds; - select_msg_t msg; + int msg; isc_boolean_t unlock_sock; int i; isc_socket_intev_t iev; @@ -627,6 +749,8 @@ watcher(void *uap) LOCK(&manager->lock); + XTRACE(("watcher got manager lock\n")); + /* * Process reads on internal, control fd. */ @@ -634,6 +758,8 @@ watcher(void *uap) while (1) { msg = select_readmsg(manager); + XTRACE(("watcher got message %d\n", msg)); + /* * Nothing to read? */ @@ -660,6 +786,8 @@ watcher(void *uap) sock = manager->fds[msg]; LOCK(&sock->lock); + XTRACE(("watcher locked socket %p\n", + sock)); /* * If there are no events, or there @@ -669,20 +797,26 @@ watcher(void *uap) * Otherwise, set it. */ iev = HEAD(sock->read_list); - if (iev == NULL || sock->pending_read) + if (iev == NULL || sock->pending_read) { FD_CLR(sock->fd, &manager->read_fds); - else + XTRACE(("watch cleared r\n")); + } else { FD_SET(sock->fd, &manager->read_fds); + XTRACE(("watch set r\n")); + } iev = HEAD(sock->write_list); - if (iev == NULL || sock->pending_write) + if (iev == NULL || sock->pending_write) { FD_CLR(sock->fd, &manager->write_fds); - else + XTRACE(("watch cleared w\n")); + } else { FD_SET(sock->fd, &manager->write_fds); + XTRACE(("watch set w\n")); + } UNLOCK(&sock->lock); } @@ -698,12 +832,16 @@ watcher(void *uap) sock = manager->fds[i]; unlock_sock = ISC_FALSE; if (FD_ISSET(i, &readfds)) { + XTRACE(("watcher r on %d, sock %p\n", + i, manager->fds[i])); unlock_sock = ISC_TRUE; LOCK(&sock->lock); dispatch_read(sock); FD_CLR(i, &manager->read_fds); } if (FD_ISSET(i, &writefds)) { + XTRACE(("watcher w on %d, sock %p\n", + i, manager->fds[i])); if (!unlock_sock) { unlock_sock = ISC_TRUE; LOCK(&sock->lock); @@ -976,7 +1114,7 @@ isc_socket_listen(isc_socket_t sock, int backlog, isc_task_t task, isc_taskaction_t action, void *arg) { isc_socket_intev_t iev; - isc_task_t ntask; + isc_task_t ntask = NULL; isc_socketmgr_t manager; manager = sock->manager; @@ -994,9 +1132,25 @@ isc_socket_listen(isc_socket_t sock, int backlog, isc_task_t task, LOCK(&sock->lock); - if (listen(sock->fd, backlog) < 0) { - isc_event_free((isc_event_t *)&iev); + if (sock->type != isc_socket_tcp) { UNLOCK(&sock->lock); + isc_event_free((isc_event_t *)&iev); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Socket is not isc_socket_tcp"); + return (ISC_R_UNEXPECTED); + } + + if (sock->listener) { + UNLOCK(&sock->lock); + isc_event_free((isc_event_t *)&iev); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Socket already listener"); + return (ISC_R_UNEXPECTED); + } + + if (listen(sock->fd, backlog) < 0) { + UNLOCK(&sock->lock); + isc_event_free((isc_event_t *)&iev); UNEXPECTED_ERROR(__FILE__, __LINE__, "listen: %s", strerror(errno)); @@ -1008,14 +1162,24 @@ isc_socket_listen(isc_socket_t sock, int backlog, isc_task_t task, */ isc_task_attach(task, &ntask); sock->references++; + sock->listener = ISC_TRUE; iev->task = ntask; iev->done_ev = NULL; iev->partial = ISC_FALSE; /* state doesn't really matter */ - iev->listener = ISC_TRUE; iev->action = action; iev->arg = arg; + /* + * poke watcher here. We still have the socket locked, so there + * is no race condition. We will keep the lock for such a short + * bit of time waking it up now or later won't matter all that much. + */ + if (EMPTY(sock->read_list)) + select_poke(manager, sock->fd); + + ENQUEUE(sock->read_list, iev, link); + UNLOCK(&sock->lock); return (ISC_R_SUCCESS);