Commit socket code so far -- nowhere near done, but I don't wanna loose work.

This commit is contained in:
Michael Graff 1998-11-03 00:54:47 +00:00
parent 35921f41cf
commit 6d05b41aae
3 changed files with 1199 additions and 0 deletions

153
bin/tests/sock_test.c Normal file
View file

@ -0,0 +1,153 @@
#include "attribute.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <isc/assertions.h>
#include <isc/memcluster.h>
#include <isc/task.h>
#include <isc/thread.h>
#include <isc/result.h>
#include <isc/socket.h>
isc_memctx_t mctx = NULL;
static isc_boolean_t
my_callback(isc_task_t task, isc_event_t event)
{
char *name = event->arg;
printf("task %s (%p)\n", name, task);
fflush(stdout);
isc_event_free(&event);
return (ISC_FALSE);
}
static isc_boolean_t
my_shutdown(isc_task_t task, isc_event_t event)
{
char *name = event->arg;
printf("shutdown %s (%p)\n", name, task);
fflush(stdout);
isc_event_free(&event);
return (ISC_TRUE);
}
int
main(int argc, char *argv[])
{
isc_taskmgr_t manager = NULL;
isc_task_t t1 = NULL, t2 = NULL;
isc_task_t t3 = NULL, t4 = NULL;
isc_event_t event;
unsigned int workers;
isc_socketmgr_t socketmgr;
isc_socket_t so1, so2;
if (argc > 1)
workers = atoi(argv[1]);
else
workers = 2;
printf("%d workers\n", workers);
INSIST(isc_memctx_create(0, 0, &mctx) == ISC_R_SUCCESS);
INSIST(isc_taskmgr_create(mctx, workers, 0, &manager) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "1", 0, &t1) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "2", 0, &t2) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "3", 0, &t3) ==
ISC_R_SUCCESS);
INSIST(isc_task_create(manager, my_shutdown, "4", 0, &t4) ==
ISC_R_SUCCESS);
printf("task 1 = %p\n", t1);
printf("task 2 = %p\n", t2);
printf("task 3 = %p\n", t3);
printf("task 4 = %p\n", t4);
socketmgr = NULL;
INSIST(isc_socketmgr_create(mctx, &socketmgr) == ISC_R_SUCCESS);
so1 = NULL;
INSIST(isc_socket_create(socketmgr, isc_socket_udp,
&so1) == ISC_R_SUCCESS);
so2 = NULL;
INSIST(isc_socket_create(socketmgr, isc_socket_udp,
&so2) == ISC_R_SUCCESS);
sleep(2);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "1",
sizeof *event);
isc_task_send(t1, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "2",
sizeof *event);
isc_task_send(t2, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "3",
sizeof *event);
isc_task_send(t3, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "4",
sizeof *event);
isc_task_send(t4, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "2",
sizeof *event);
isc_task_send(t2, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "3",
sizeof *event);
isc_task_send(t3, &event);
event = isc_event_allocate(mctx, (void *)main, 1, my_callback, "4",
sizeof *event);
isc_task_send(t4, &event);
isc_task_purge(t3, NULL, 0);
isc_task_detach(&t1);
isc_task_detach(&t2);
isc_task_detach(&t3);
isc_task_detach(&t4);
sleep(10);
printf("destroy\n");
isc_socket_detach(&so1);
isc_socket_detach(&so2);
isc_socketmgr_destroy(&socketmgr);
isc_taskmgr_destroy(&manager);
printf("destroyed\n");
isc_mem_stats(mctx, stdout);
isc_memctx_destroy(&mctx);
return (0);
}

View file

@ -0,0 +1,522 @@
#ifndef ISC_SOCKET_H
#define ISC_SOCKET_H 1
/*****
***** Module Info
*****/
/*
* Sockets
*
* Provides TCP and UDP sockets for network I/O. The sockets are event
* sources in the task system.
*
* When I/O completes, a completion event for the socket is posted to the
* event queue of the task which requested the I/O.
*
* MP:
* The module ensures appropriate synchronization of data structures it
* creates and manipulates.
*
* Clients of this module must not be holding a socket's task's lock when
* making a call that affects that socket. Failure to follow this rule
* can result in deadlock.
*
* The caller must ensure that isc_socketmgr_destroy() is called only
* once for a given manager.
*
* Reliability:
* No anticipated impact.
*
* Resources:
* <TBS>
*
* Security:
* No anticipated impact.
*
* Standards:
* None.
*/
/***
*** Imports
***/
#include <isc/boolean.h>
#include <isc/result.h>
#include <isc/task.h>
#include <isc/region.h>
#include <isc/memcluster.h>
#include <netinet/in.h>
/***
*** Types
***/
typedef struct isc_socket *isc_socket_t;
typedef struct isc_socketmgr *isc_socketmgr_t;
/*
* XXX Export this as isc/sockaddr.h
*/
typedef struct isc_sockaddr {
/*
* XXX Must be big enough for all sockaddr types we care about.
*/
union foo {
struct sockaddr_in sin;
};
} *isc_sockaddr_t;
typedef struct isc_sockevent {
struct isc_event common; /* Sender is the socket. */
isc_result_t result; /* OK, EOF, whatever else */
unsigned int n; /* bytes read or written */
struct isc_sockaddr address; /* source address */
int length; /* length of address */
} *isc_sockevent_t;
#define ISC_SOCKEVENT_ANYEVENT (0)
#define ISC_SOCKEVENT_RECVDONE (EVENT_CLASS_SOCKET + 1)
#define ISC_SOCKEVENT_SENDDONE (EVENT_CLASS_SOCKET + 2)
#define ISC_SOCKEVENT_NEWCONN (EVENT_CLASS_SOCKET + 3)
#define ISC_SOCKEVENT_CONNECTED (EVENT_CLASS_SOCKET + 4)
typedef enum {
isc_socket_udp,
isc_socket_tcp
} isc_sockettype_t;
typedef enum {
isc_sockshut_reading,
isc_sockshut_writing,
isc_sockshut_all
} isc_socketshutdown_t;
/***
*** Socket and Socket Manager Functions
***
*** Note: all Ensures conditions apply only if the result is success for
*** those functions which return an isc_result.
***/
isc_result_t
isc_socket_create(isc_socketmgr_t manager,
isc_sockettype_t type,
isc_socket_t *socketp);
/*
* Create a new 'type' socket managed by 'manager'.
*
* Requires:
*
* 'manager' is a valid manager
*
* 'socketp' is a valid pointer, and *socketp == NULL
*
* Ensures:
*
* '*socketp' is attached to the newly created socket
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
void
isc_socket_shutdown(isc_socket_t socket, isc_task_t task,
isc_socketshutdown_t how);
/*
* Shutdown 'socket' according to 'how'.
*
* Note: if 'task' is NULL, then the shutdown applies to all tasks using the
* socket.
*
* Requires:
*
* 'socket' is a valid socket.
*
* 'task' is NULL or is a valid task.
*
* Ensures:
*
* If 'how' is 'isc_sockshut_reading' or 'isc_sockshut_all' then
*
* Any pending read completion events for the task are
* removed from the task's event queue.
*
* No further read completion events will be delivered to the
* task.
*
* No further read requests may be made.
*
* If 'how' is 'isc_sockshut_writing' or 'isc_sockshut_all' then
*
* Any pending write completion events for the task are
* removed from the task's event queue.
*
* No further write completion events will be delivered to the
* task.
*
* No further write requests may be made.
*
* If 'socket' is a TCP socket, then when the last currently
* pending write completes, TCP FIN will sent to the remote peer.
*/
void
isc_socket_attach(isc_socket_t socket, isc_socket_t *socketp);
/*
* Attach *socketp to socket.
*
* Requires:
*
* 'socket' is a valid socket.
*
* 'socketp' points to a NULL socket.
*
* Ensures:
*
* *socketp is attached to socket.
*/
void
isc_socket_detach(isc_socket_t *socketp);
/*
* Detach *socketp from its socket.
*
* Notes:
*
* Detaching the last reference may cause any still-pending I/O to be
* cancelled.
*
* Requires:
*
* 'socketp' points to a valid socket.
*
* Ensures:
*
* *socketp is NULL.
*
* If '*socketp' is the last reference to the socket,
* then:
*
* The socket will be shutdown (both reading and writing)
* for all tasks.
*
* All resources used by the socket have been freed
*/
isc_result_t
net_socket_bind(isc_socket_t socket, struct isc_sockaddr *addressp,
int length);
/*
* Bind 'socket' to '*addressp'.
*
* Requires:
*
* 'socket' is a valid socket
*
* 'addressp' points to a valid isc_sockaddr.
*
* 'length' is approprate for the isc_sockaddr type.
*
* Returns:
*
* Success
* Address not available
* Address in use
* Permission denied
* Unexpected error
*/
isc_result_t
isc_socket_listen(isc_socket_t socket, int backlog,
isc_task_t task, isc_taskaction_t action, void *arg);
/*
* Listen on 'socket'. Every time a new connection request arrives,
* a NEWCONN event with action 'action' and arg 'arg' will be posted
* to the event queue for 'task'.
*
* Notes:
*
* 'backlog' is as in the UNIX system call listen().
*
* Requires:
*
* 'socket' is a valid TCP socket.
*
* 'task' is a valid task
*
* 'action' is a valid action
*
* Returns:
*
* Success
* Unexpected error
*/
void
isc_socket_hold(isc_socket_t socket);
/*
* Put a TCP listener socket on hold. No NEWCONN events will be posted
*
* Notes:
*
* While 'on hold', new connection requests will be queued or dropped
* by the operating system.
*
* Requires:
*
* 'socket' is a valid TCP socket
*
* Some task is listening on the socket.
*
*/
void
isc_socket_unhold(isc_socket_t socket);
/*
* Restore normal NEWCONN event posting.
*
* Requires:
*
* 'socket' is a valid TCP socket
*
* Some task is listening on the socket.
*
* 'socket' is holding.
*
*/
isc_result_t
isc_socket_accept(isc_socket_t s1, isc_socket_t *s2p);
/*
* Accept a connection from 's1', creating a new socket for the connection
* and attaching '*s2p' to it.
*
* Requires:
*
* 'socket' is a valid TCP socket.
*
* s2p is a valid pointer, and *s2p == NULL;
*
* Ensures:
*
* *s2p is attached to the newly created socket.
*
* Returns:
*
* Success
* No memory
* No pending connection requests
* Unexpected error
*/
isc_result_t
isc_socket_connect(isc_socket_t socket, struct isc_sockaddr *addressp,
int length, isc_task_t task, isc_taskaction_t action,
void *arg);
/*
* Connect 'socket' to peer with address *saddr. When the connection
* succeeds, or when an error occurs, a CONNECTED event with action 'action'
* and arg 'arg' will be posted to the event queue for 'task'.
*
* Requires:
*
* 'socket' is a valid TCP socket
*
* 'addressp' points to a valid isc_sockaddr
*
* 'length' is approprate for the isc_sockaddr type
*
* 'task' is a valid task
*
* 'action' is a valid action
*
* Returns:
*
* Success
* No memory
* Address not available
* Address in use
* Host unreachable
* Network unreachable
* Connection refused
* Unexpected error
*/
isc_result_t
isc_socket_getpeername(isc_socket_t socket, struct isc_sockaddr *addressp,
int *lengthp);
/*
* Get the name of the peer connected to 'socket'.
*
* Requires:
*
* 'socket' is a valid TCP socket.
*
* 'addressp' points to '*lengthp' bytes.
*
* Returns:
*
* Success
* Address buffer too small
*/
isc_result_t
isc_socket_getsockname(isc_socket_t socket, struct isc_sockaddr *addressp,
int *lengthp);
/*
* Get the name of 'socket'.
*
* Requires:
*
* 'socket' is a valid socket.
*
* 'addressp' points to '*lengthp' bytes.
*
* Returns:
*
* Success
* Address buffer too small
*/
isc_result_t
isc_socket_recv(isc_socket_t socket, isc_region_t region,
isc_boolean_t partial,
isc_task_t task, isc_taskaction_t action, void *arg);
/*
* Receive from 'socket', storing the results in region.
*
* Notes:
*
* Let 'length' refer to the length of 'region'.
*
* If 'partial' is true, then at most 'length' bytes will be read.
* Otherwise the read will not complete until exactly 'length' bytes
* have been read.
*
* The read will complete when the desired number of bytes have been
* read, if end-of-input occurs, or if an error occurs. A read done
* event with the given 'action' and 'arg' will be posted to the
* event queue of 'task'.
*
* The caller may neither read from nor write to 'region' until it
* has received the read completion event.
*
* Requires:
*
* 'socket' is a valid socket
*
* 'region' is a valid region
*
* 'task' is a valid task
*
* action != NULL and is a valid action
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
isc_result_t
isc_socket_send(isc_socket_t socket, isc_region_t region,
isc_task_t task, isc_taskaction_t action, void *arg);
/*
* Send the contents of 'region' to the socket's peer.
*
* Notes:
*
* Shutting down the requestor's task *may* result in any
* still pending writes being dropped.
*
* If 'action' is NULL, then no completion event will be posted.
*
* The caller may neither read from nor write to 'region' until it
* has received the write completion event, or all references to the
* socket have been detached.
*
* Requires:
*
* 'socket' is a valid socket
*
* 'region' is a valid region
*
* 'task' is a valid task
*
* action == NULL or is a valid action
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
/* XXX this is some of how to do a read
* generate new net_request
* generate new read-result net_event
* attach to requestor
* lock socket
* queue request
* unlock socket
*/
isc_result_t
isc_socketmgr_create(isc_memctx_t mctx, isc_socketmgr_t *managerp);
/*
* Create a socket manager.
*
* Notes:
*
* All memory will be allocated in memory context 'mctx'.
*
* Requires:
*
* 'mctx' is a valid memory context.
*
* 'managerp' points to a NULL isc_socketmgr_t.
*
* Ensures:
*
* '*managerp' is a valid isc_socketmgr_t.
*
* Returns:
*
* Success
* No memory
* Unexpected error
*/
void
isc_socketmgr_destroy(isc_socketmgr_t *);
/*
* Destroy a socket manager.
*
* Notes:
*
* This routine blocks until there are no sockets left in the manager,
* so if the caller holds any socket references using the manager, it
* must detach them before calling isc_socketmgr_destroy() or it will
* block forever.
*
* Requires:
*
* '*managerp' is a valid isc_socketmgr_t.
*
* Ensures:
*
* *managerp == NULL
*
* All resources used by the manager have been freed.
*/
#endif /* ISC_SOCKET_H */

524
lib/isc/unix/socket.c Normal file
View file

@ -0,0 +1,524 @@
#include "attribute.h"
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <isc/assertions.h>
#include <isc/unexpect.h>
#include <isc/thread.h>
#include <isc/mutex.h>
#include <isc/condition.h>
#include <isc/socket.h>
#ifndef _WIN32
#define WINAPI /* we're not windows */
#endif
/*
* We use macros instead of calling the routines directly because
* the capital letters make the locking stand out.
*
* We INSIST that they succeed since there's no way for us to continue
* if they fail.
*/
#define LOCK(lp) \
INSIST(isc_mutex_lock((lp)) == ISC_R_SUCCESS);
#define UNLOCK(lp) \
INSIST(isc_mutex_unlock((lp)) == ISC_R_SUCCESS);
#define BROADCAST(cvp) \
INSIST(isc_condition_broadcast((cvp)) == ISC_R_SUCCESS);
#define SIGNAL(cvp) \
INSIST(isc_condition_signal((cvp)) == ISC_R_SUCCESS);
#define WAIT(cvp, lp) \
INSIST(isc_condition_wait((cvp), (lp)) == ISC_R_SUCCESS);
#define WAITUNTIL(cvp, lp, tp) \
isc_condition_waituntil((cvp), (lp), (tp))
/*
* 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))
#else
#define XTRACE(a)
#define XENTER(a)
#define XEXIT(a)
#endif
/*
* A socket request. These are allocated XXX
*/
struct isc_socket_req {
isc_task_t task;
};
#define SOCKET_MAGIC 0x494f696fU /* IOio */
#define VALID_SOCKET(t) ((t) != NULL && \
(t)->magic == SOCKET_MAGIC)
struct isc_socket {
/* Not locked. */
unsigned int magic;
isc_socketmgr_t manager;
isc_mutex_t lock;
/* Locked by socket lock. */
unsigned int references;
int fd;
LIST(struct isc_socket_req) read_reqs;
LIST(struct isc_socket_req) write_reqs;
/* Locked by manager lock. */
isc_sockettype_t type;
LINK(struct isc_socket) link;
};
#define SOCKET_MANAGER_MAGIC 0x494f6d67U /* IOmg */
#define VALID_MANAGER(m) ((m) != NULL && \
(m)->magic == SOCKET_MANAGER_MAGIC)
struct isc_socketmgr {
/* Not locked. */
unsigned int magic;
isc_memctx_t mctx;
isc_mutex_t lock;
/* Locked by manager lock. */
isc_boolean_t done;
LIST(struct isc_socket) sockets;
unsigned int nscheduled;
isc_thread_t thread;
int pipe_fds[2]; /* XXX lock needed? */
fd_set read_fds; /* XXX This sucks... */
fd_set write_fds;
};
#define SELECT_POKE_SHUTDOWN (1)
#define SELECT_POKE_REFRESH (2)
typedef unsigned long select_msg_t;
/*
* poke the select loop when there is something for us to do.
*/
static void
select_poke(isc_socketmgr_t mgr, select_msg_t msg)
{
write(mgr->pipe_fds[1], &msg, sizeof(select_msg_t));
}
/*
* read a message on the internal fd.
*/
static select_msg_t
select_readmsg(isc_socketmgr_t mgr)
{
select_msg_t msg;
read(mgr->pipe_fds[0], &msg, sizeof(select_msg_t));
return msg;
}
/*
* Set a socket up for reading or writing. This is a low level, internal
* routine.
*
* Caller must ensure locking.
*/
static inline isc_result_t
schedule(isc_socket_t sock)
{
isc_result_t result;
isc_socketmgr_t manager;
/*
* do stuff here to arange to track I/O on this socket.
*/
return (ISC_R_SUCCESS);
}
/*
* Remove either read, write, or both from a socket.
*
* Caller must ensure locking.
*/
static inline void
deschedule(isc_socket_t sock)
{
isc_boolean_t need_wakeup = ISC_FALSE;
isc_socketmgr_t manager;
manager = sock->manager;
}
/*
* Kill.
*
* Caller must ensure locking.
*/
static void
destroy(isc_socket_t sock)
{
isc_socketmgr_t manager = sock->manager;
LOCK(&manager->lock);
/*
* XXX
* This is going to be tricky... Run through the list of all
* tasks attached to this socket and purge events in their
* queues.
*/
deschedule(sock);
UNLINK(manager->sockets, sock, link);
UNLOCK(&manager->lock);
(void)isc_mutex_destroy(&sock->lock);
sock->magic = 0;
isc_mem_put(manager->mctx, sock, sizeof *sock);
}
/*
* Create a new 'type' socket managed by 'manager'. The sockets
* parameters are specified by 'expires' and 'interval'. Events
* will be posted to 'task' and when dispatched 'action' will be
* called with 'arg' as the arg value. The new socket is returned
* in 'socketp'.
*/
isc_result_t
isc_socket_create(isc_socketmgr_t manager, isc_sockettype_t type,
isc_socket_t *socketp)
{
isc_socket_t sock;
isc_result_t result;
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_reqs);
INIT_LIST(sock->write_reqs);
/*
* Create the associated socket XXX
*/
switch (type) {
case isc_socket_udp:
sock->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
break;
case isc_socket_tcp:
sock->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
break;
}
if (sock->fd < 0) {
isc_mem_put(manager->mctx, sock, sizeof *sock);
switch (errno) {
case EMFILE:
case ENFILE:
case ENOBUFS:
return (ISC_R_NORESOURCES);
break;
default:
UNEXPECTED_ERROR(__FILE__, __LINE__,
"socket() failed: %s",
strerror(errno));
return (ISC_R_UNEXPECTED);
break;
}
}
/*
* 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);
/*
* Note we don't have to lock the socket like we normally would because
* there are no external references to it yet.
*/
APPEND(manager->sockets, sock, link);
result = schedule(sock);
UNLOCK(&manager->lock);
if (result == ISC_R_SUCCESS)
*socketp = sock;
XEXIT("isc_socket_create");
return (result);
}
/*
* Attach to a socket. Caller must explicitly detach when it is done.
*/
void
isc_socket_attach(isc_socket_t sock, isc_socket_t *socketp)
{
REQUIRE(VALID_SOCKET(sock));
REQUIRE(socketp != NULL && *socketp == NULL);
LOCK(&sock->lock);
sock->references++;
UNLOCK(&sock->lock);
*socketp = sock;
}
/*
* Dereference a socket. If this is the last reference to it, clean things
* up by destroying the socket.
*/
void
isc_socket_detach(isc_socket_t *socketp)
{
isc_socket_t sock;
isc_boolean_t free_socket = ISC_FALSE;
REQUIRE(socketp != NULL);
sock = *socketp;
REQUIRE(VALID_SOCKET(sock));
XENTER("isc_socket_detach");
LOCK(&sock->lock);
REQUIRE(sock->references > 0);
sock->references--;
if (sock->references == 0)
free_socket = ISC_TRUE;
UNLOCK(&sock->lock);
if (free_socket)
destroy(sock);
XEXIT("isc_socket_detach");
*socketp = NULL;
}
static void
dispatch(isc_socketmgr_t manager, isc_socket_t sock)
{
isc_boolean_t done = ISC_FALSE;
isc_boolean_t post_event;
isc_boolean_t need_schedule;
isc_event_t event;
isc_eventtype_t type = 0;
isc_result_t result;
while (manager->nscheduled > 0 && !done) {
/*
* Do what here? XXX
*/
}
}
/*
* This is the task that will loop forever, always in a select or poll call.
* When select returns something to do, track down what thread gets to do
* this I/O and post the event to it.
*/
static isc_threadresult_t
WINAPI
run(void *uap)
{
isc_socketmgr_t manager = uap;
isc_boolean_t done;
int ctlfd;
int cc;
fd_set readfds;
fd_set writefds;
select_msg_t msg;
/*
* Get the control fd here. This will never change.
*/
LOCK(&manager->lock);
ctlfd = manager->pipe_fds[0];
done = ISC_FALSE;
while (!done) {
readfds = manager->read_fds;
writefds = manager->write_fds;
UNLOCK(&manager->lock);
/*
* call select/poll. This will block. XXX flesh out
*/
cc = select(FD_SETSIZE, &readfds, &writefds, NULL, NULL);
if (cc < 0) {
if (errno != EINTR)
UNEXPECTED_ERROR(__FILE__, __LINE__,
"select returned error (%s)",
strerror(errno));
}
LOCK(&manager->lock);
/*
* Process reads on internal, control fd.
*/
if (FD_ISSET(ctlfd, &readfds)) {
msg = select_readmsg(manager);
/*
* handle shutdown message. No other type is handled
* here, as REFRESH tells us to reread our fd_sets,
* which we always do at the top of the loop.
*/
if (msg == SELECT_POKE_SHUTDOWN)
done = ISC_TRUE;
}
/*
* Process read/writes on other fds here
*/
}
UNLOCK(&manager->lock);
return ((isc_threadresult_t)0);
}
/*
* Create a new socket manager.
*/
isc_result_t
isc_socketmgr_create(isc_memctx_t mctx, isc_socketmgr_t *managerp)
{
isc_socketmgr_t manager;
REQUIRE(managerp != NULL && *managerp == NULL);
XENTER("isc_socketmgr_create");
manager = isc_mem_get(mctx, sizeof *manager);
if (manager == NULL)
return (ISC_R_NOMEMORY);
manager->magic = SOCKET_MANAGER_MAGIC;
manager->mctx = mctx;
manager->done = ISC_FALSE;
INIT_LIST(manager->sockets);
manager->nscheduled = 0;
if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
isc_mem_put(mctx, manager, sizeof *manager);
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_mutex_init() failed");
return (ISC_R_UNEXPECTED);
}
/*
* Create the special fds that will be used to wake up the
* select/poll loop when something internal needs to be done.
*/
if (pipe(manager->pipe_fds) != 0) {
(void)isc_mutex_destroy(&manager->lock);
isc_mem_put(mctx, manager, sizeof *manager);
UNEXPECTED_ERROR(__FILE__, __LINE__,
"pipe() failed: %s",
strerror(errno)); /* XXX */
return (ISC_R_UNEXPECTED);
}
/*
* Set up initial state for the select loop
*/
FD_ZERO(&manager->read_fds);
FD_ZERO(&manager->write_fds);
FD_SET(manager->pipe_fds[0], &manager->read_fds);
/*
* Start up the select/poll thread.
*/
if (isc_thread_create(run, manager, &manager->thread) !=
ISC_R_SUCCESS) {
(void)isc_mutex_destroy(&manager->lock);
isc_mem_put(mctx, manager, sizeof *manager);
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_thread_create() failed");
return (ISC_R_UNEXPECTED);
}
*managerp = manager;
XEXIT("isc_socketmgr_create (normal)");
return (ISC_R_SUCCESS);
}
void
isc_socketmgr_destroy(isc_socketmgr_t *managerp)
{
isc_socketmgr_t manager;
/*
* Destroy a socket manager.
*/
REQUIRE(managerp != NULL);
manager = *managerp;
REQUIRE(VALID_MANAGER(manager));
LOCK(&manager->lock);
REQUIRE(EMPTY(manager->sockets));
manager->done = ISC_TRUE;
UNLOCK(&manager->lock);
/*
* Here, poke our select/poll thread. Do this by closing the write
* half of the pipe, which will send EOF to the read half.
*/
select_poke(manager, SELECT_POKE_SHUTDOWN);
/*
* Wait for thread to exit.
*/
if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_thread_join() failed");
/*
* Clean up.
*/
close(manager->pipe_fds[0]);
close(manager->pipe_fds[1]);
(void)isc_mutex_destroy(&manager->lock);
manager->magic = 0;
isc_mem_put(manager->mctx, manager, sizeof *manager);
*managerp = NULL;
}