From fb06cccfe24ee6d3569268700a7330d84e7a7b4f Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Sun, 6 Jun 2004 22:30:10 +0000 Subject: [PATCH] * New session scheduler. All sessions which select() marks for activity are now handled before select() is called again. --- citadel/ChangeLog | 5 +- citadel/server.h | 13 +++- citadel/server_main.c | 13 ---- citadel/sysdep.c | 156 +++++++++++++++--------------------------- 4 files changed, 71 insertions(+), 116 deletions(-) diff --git a/citadel/ChangeLog b/citadel/ChangeLog index a7a49fe05..2f66c817a 100644 --- a/citadel/ChangeLog +++ b/citadel/ChangeLog @@ -1,4 +1,8 @@ $Log$ + Revision 621.7 2004/06/06 22:30:10 ajc + * New session scheduler. All sessions which select() marks for activity + are now handled before select() is called again. + Revision 621.6 2004/06/03 02:49:14 ajc * html.c: allow parsing of tags even when they're qualified (i.e. instead of just ) @@ -5812,4 +5816,3 @@ Sat Jul 11 00:20:48 EDT 1998 Nathan Bryant Fri Jul 10 1998 Art Cancro * Initial CVS import - diff --git a/citadel/server.h b/citadel/server.h index d1ec072c8..1e8eea5a3 100644 --- a/citadel/server.h +++ b/citadel/server.h @@ -148,10 +148,19 @@ struct CitContext { typedef struct CitContext t_context; -/* Values for CitContext.state */ +/* + * Values for CitContext.state + * + * A session that is doing nothing is in CON_IDLE state. When activity + * is detected on the socket, it goes to CON_READY, indicating that it + * needs to have a worker thread bound to it. When a thread binds to + * the session, it goes to CON_EXECUTING and does its thing. When the + * transaction is finished, the thread sets it back to CON_IDLE and lets + * it go. + */ enum { CON_IDLE, /* This context is doing nothing */ - CON_READY, /* This context is ready-to-run */ + CON_READY, /* This context needs attention */ CON_EXECUTING, /* This context is bound to a thread */ }; diff --git a/citadel/server_main.c b/citadel/server_main.c index ebf85cdf1..0472d49c4 100644 --- a/citadel/server_main.c +++ b/citadel/server_main.c @@ -190,19 +190,6 @@ int main(int argc, char **argv) size = strlen(bbs_home_directory) + 9; initialize_server_extensions(); - /* - * The rescan pipe exists so that worker threads can be woken up and - * told to re-scan the context list for fd's to listen on. This is - * necessary, for example, when a context is about to go idle and needs - * to get back on that list. - */ - if (pipe(rescan)) { - lprintf(CTDL_EMERG, "Can't create rescan pipe!\n"); - exit(errno); - } - - init_master_fdset(); - /* * Now that we've bound the sockets, change to the BBS user id and its * corresponding group ids diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 9c2816735..3b1c54b89 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -91,20 +91,14 @@ pthread_key_t MyConKey; /* TSD key for MyContext() */ int verbosity = DEFAULT_VERBOSITY; /* Logging level */ struct CitContext masterCC; -int rescan[2]; /* The Rescan Pipe */ time_t last_purge = 0; /* Last dead session purge */ static int num_threads = 0; /* Current number of threads */ int num_sessions = 0; /* Current number of sessions */ -fd_set masterfds; /* Master sockets etc. */ -int masterhighest; - pthread_t initial_thread; /* tid for main() thread */ int syslog_facility = (-1); -/* This is synchronized below; it helps implement round robin mode */ -extern struct CitContext* next_session; /* * lprintf() ... Write logging information @@ -858,37 +852,7 @@ void InitializeMasterCC(void) { -/* - * Set up a fd_set containing all the master sockets to which we - * always listen. It's computationally less expensive to just copy - * this to a local fd_set when starting a new select() and then add - * the client sockets than it is to initialize a new one and then - * figure out what to put there. - */ -void init_master_fdset(void) { - struct ServiceFunctionHook *serviceptr; - int m; - - lprintf(CTDL_DEBUG, "Initializing master fdset\n"); - FD_ZERO(&masterfds); - masterhighest = 0; - - lprintf(CTDL_DEBUG, "Will listen on rescan pipe %d\n", rescan[0]); - FD_SET(rescan[0], &masterfds); - if (rescan[0] > masterhighest) masterhighest = rescan[0]; - - for (serviceptr = ServiceHookTable; serviceptr != NULL; - serviceptr = serviceptr->next ) { - m = serviceptr->msock; - lprintf(CTDL_DEBUG, "Will listen on master socket %d\n", m); - FD_SET(m, &masterfds); - if (m > masterhighest) { - masterhighest = m; - } - } - lprintf(CTDL_DEBUG, "masterhighest = %d\n", masterhighest); -} /* @@ -905,7 +869,6 @@ INLINE void become_session(struct CitContext *which_con) { */ void *worker_thread(void *arg) { int i; - char junk; int highest; struct CitContext *ptr; struct CitContext *bind_me = NULL; @@ -916,6 +879,7 @@ void *worker_thread(void *arg) { int ssock; /* Descriptor for client socket */ struct timeval tv; int force_purge = 0; + int m; num_threads++; @@ -940,10 +904,14 @@ void *worker_thread(void *arg) { */ cdb_check_handles(); force_purge = 0; + bind_me = NULL; /* Which session shall we handle? */ begin_critical_section(S_I_WANNA_SELECT); -SETUP_FD: memcpy(&readfds, &masterfds, sizeof masterfds); - highest = masterhighest; + + /* Initialize the fdset. */ + FD_ZERO(&readfds); + highest = 0; + begin_critical_section(S_SESSION_TABLE); for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { if (ptr->state == CON_IDLE) { @@ -951,15 +919,32 @@ SETUP_FD: memcpy(&readfds, &masterfds, sizeof masterfds); if (ptr->client_socket > highest) highest = ptr->client_socket; } + if ((bind_me == NULL) && (ptr->state == CON_READY)) { + bind_me = ptr; + ptr->state = CON_EXECUTING; + } } end_critical_section(S_SESSION_TABLE); - tv.tv_sec = 1; /* wake up every second if no input */ - tv.tv_usec = 0; + if (bind_me) goto SKIP_SELECT; + +do_select: /* Only select() if the server isn't being told to shut down. */ + + /* Add the various master sockets to the fdset. */ + for (serviceptr = ServiceHookTable; serviceptr != NULL; + serviceptr = serviceptr->next ) { + m = serviceptr->msock; + FD_SET(m, &readfds); + if (m > highest) { + highest = m; + } + } - do_select: - if (!time_to_die) + if (!time_to_die) { + tv.tv_sec = 1; /* wake up every second if no input */ + tv.tv_usec = 0; retval = select(highest + 1, &readfds, NULL, NULL, &tv); + } else { end_critical_section(S_I_WANNA_SELECT); break; @@ -972,7 +957,7 @@ SETUP_FD: memcpy(&readfds, &masterfds, sizeof masterfds); if (errno == EBADF) { lprintf(CTDL_NOTICE, "select() failed: (%s)\n", strerror(errno)); - goto SETUP_FD; + goto do_select; } if (errno != EINTR) { lprintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno)); @@ -1024,7 +1009,7 @@ SETUP_FD: memcpy(&readfds, &masterfds, sizeof masterfds); serviceptr->h_greeting_function(); become_session(NULL); con->state = CON_IDLE; - goto SETUP_FD; + goto do_select; } } } @@ -1034,66 +1019,37 @@ SETUP_FD: memcpy(&readfds, &masterfds, sizeof masterfds); break; } - /* If the rescan pipe went active, someone is telling this - * thread that the &readfds needs to be refreshed with more - * current data. - */ - if (FD_ISSET(rescan[0], &readfds)) { - read(rescan[0], &junk, 1); - goto SETUP_FD; - } - /* It must be a client socket. Find a context that has data - * waiting on its socket *and* is in the CON_IDLE state. + * waiting on its socket *and* is in the CON_IDLE state. Any + * active sockets other than our chosen one are marked as + * CON_READY so the next thread that comes around can just bind + * to one without having to select() again. */ - else { - bind_me = NULL; - begin_critical_section(S_SESSION_TABLE); - /* - * We start where we left off. If we get to the end - * we'll start from the beginning again, then give up - * if we still don't find anything. This ensures - * that all contexts get a more-or-less equal chance - * to run. And yes, I did add a goto to the code. -IO - */ -find_session: if (next_session == NULL) - next_session = ContextList; - for (ptr = next_session; - ( (ptr != NULL) && (bind_me == NULL) ); - ptr = ptr->next) { - if ( (FD_ISSET(ptr->client_socket, &readfds)) - && (ptr->state == CON_IDLE) ) { - bind_me = ptr; + begin_critical_section(S_SESSION_TABLE); + for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { + if ( (FD_ISSET(ptr->client_socket, &readfds)) + && (ptr->state != CON_EXECUTING) ) { + if (!bind_me) { + bind_me = ptr; /* I choose you! */ + bind_me->state = CON_EXECUTING; + } + else { + ptr->state = CON_READY; } } - if (bind_me != NULL) { - /* Found one. Stake a claim to it before - * letting anyone else touch the context list. - */ - bind_me->state = CON_EXECUTING; - next_session = bind_me->next; - } else if (next_session == ContextList) { - next_session = NULL; - } - if (bind_me == NULL && next_session != NULL) { - next_session = NULL; - goto find_session; - } - - end_critical_section(S_SESSION_TABLE); - end_critical_section(S_I_WANNA_SELECT); - - /* We're bound to a session, now do *one* command */ - if (bind_me != NULL) { - become_session(bind_me); - CC->h_command_function(); - force_purge = CC->kill_me; - become_session(NULL); - bind_me->state = CON_IDLE; - write(rescan[1], &junk, 1); - } - } + end_critical_section(S_SESSION_TABLE); +SKIP_SELECT: end_critical_section(S_I_WANNA_SELECT); + + /* We're bound to a session, now do *one* command */ + if (bind_me != NULL) { + become_session(bind_me); + CC->h_command_function(); + force_purge = CC->kill_me; + become_session(NULL); + bind_me->state = CON_IDLE; + } + dead_session_purge(force_purge); do_housekeeping(); check_sched_shutdown(); -- 2.39.2