From: Art Cancro Date: Wed, 27 Oct 1999 04:26:59 +0000 (+0000) Subject: * Initial hack of worker-thread rearchitecture. Right now it is successfully X-Git-Tag: v7.86~7480 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=42412e16acbac1716baa03490d271b59f21dbd93 * Initial hack of worker-thread rearchitecture. Right now it is successfully dispatching worker threads to active client sockets (and to the master socket too, of course). Removing sessions is currently broken. There is also a problem with worker threads waking up too quickly when a client command is entered (race condition?). More cleanup to follow. --- diff --git a/citadel/ChangeLog b/citadel/ChangeLog index 1ab0cbaa1..0040631e7 100644 --- a/citadel/ChangeLog +++ b/citadel/ChangeLog @@ -1,4 +1,11 @@ $Log$ +Revision 1.398 1999/10/27 04:26:58 ajc +* Initial hack of worker-thread rearchitecture. Right now it is successfully + dispatching worker threads to active client sockets (and to the master + socket too, of course). Removing sessions is currently broken. There is + also a problem with worker threads waking up too quickly when a client + command is entered (race condition?). More cleanup to follow. + Revision 1.397 1999/10/26 20:20:29 ajc * Removed the auto-reconnect stuff... it was locking the client in an active loop more often than it was reconnecting. @@ -1364,3 +1371,4 @@ Sat Jul 11 00:20:48 EDT 1998 Nathan Bryant Fri Jul 10 1998 Art Cancro * Initial CVS import + diff --git a/citadel/citserver.c b/citadel/citserver.c index 738e86695..cd791e358 100644 --- a/citadel/citserver.c +++ b/citadel/citserver.c @@ -122,9 +122,9 @@ void deallocate_user_data(struct CitContext *con) * All NON-system-dependent stuff is done in this function. * System-dependent session/thread cleanup is in cleanup() in sysdep.c */ -void cleanup_stuff(void *arg) +void cleanup(int exit_code) { - lprintf(9, "cleanup_stuff() called\n"); + lprintf(9, "cleanup(%d) called\n", exit_code); lprintf(7, "Calling logout(%d)\n", CC->cs_pid); logout(CC); @@ -141,13 +141,8 @@ void cleanup_stuff(void *arg) /* Deallocate any user-data attached to this session */ deallocate_user_data(CC); - /* Tell the housekeeping thread to remove the session and context. - * This can't be done inline because the context data gets destroyed - * halfway through, and the context being destroyed can't be the one - * doing the work. - */ - lprintf(7, "Calling RemoveContext(%d)\n", CC->cs_pid); - RemoveContext(CC); + /* And flag the context as in need of being killed */ + CC->state = CON_DYING; } @@ -801,460 +796,464 @@ void cmd_scdn(char *argbuf) /* - * main context loop + * */ -void *context_loop(struct CitContext *con) +void begin_session(struct CitContext *con) { - char cmdbuf[256]; int num_sessions, len; struct sockaddr_in sin; - /* - * Wedge our way into the context table. - */ - InitMyContext(con); - /* * Initialize some variables specific to our context. */ - CC->logged_in = 0; - CC->internal_pgm = 0; - CC->download_fp = NULL; - CC->upload_fp = NULL; - CC->cs_pid = con->client_socket; /* not necessarily portable */ - CC->FirstExpressMessage = NULL; - time(&CC->lastcmd); - time(&CC->lastidle); - strcpy(CC->lastcmdname, " "); - strcpy(CC->cs_clientname, "(unknown)"); - strcpy(CC->curr_user,"(not logged in)"); - strcpy(CC->net_node,""); - snprintf(CC->temp, sizeof CC->temp, tmpnam(NULL)); - safestrncpy(CC->cs_host, config.c_fqdn, sizeof CC->cs_host); - CC->cs_host[sizeof CC->cs_host - 1] = 0; + con->logged_in = 0; + con->internal_pgm = 0; + con->download_fp = NULL; + con->upload_fp = NULL; + con->cs_pid = con->client_socket; /* not necessarily portable */ + con->FirstExpressMessage = NULL; + time(&con->lastcmd); + time(&con->lastidle); + strcpy(con->lastcmdname, " "); + strcpy(con->cs_clientname, "(unknown)"); + strcpy(con->curr_user,"(not logged in)"); + strcpy(con->net_node,""); + snprintf(con->temp, sizeof con->temp, tmpnam(NULL)); + safestrncpy(con->cs_host, config.c_fqdn, sizeof con->cs_host); + con->cs_host[sizeof con->cs_host - 1] = 0; len = sizeof sin; - if (!getpeername(CC->client_socket, (struct sockaddr *) &sin, &len)) - locate_host(CC->cs_host, &sin.sin_addr); - CC->cs_flags = 0; - CC->upload_type = UPL_FILE; - CC->dl_is_net = 0; - CC->FirstSessData = NULL; + if (!getpeername(con->client_socket, (struct sockaddr *) &sin, &len)) + locate_host(con->cs_host, &sin.sin_addr); + con->cs_flags = 0; + con->upload_type = UPL_FILE; + con->dl_is_net = 0; + con->FirstSessData = NULL; num_sessions = session_count(); - CC->nologin = 0; + con->nologin = 0; if ((config.c_maxsessions > 0)&&(num_sessions > config.c_maxsessions)) - CC->nologin = 1; + con->nologin = 1; - if (CC->nologin==1) { - cprintf("%d %s: Too many users are already online (maximum is %d)\n", - ERROR+MAX_SESSIONS_EXCEEDED, - config.c_nodename,config.c_maxsessions); + if (con->nologin==1) { + cprintf("%d %s: Too many users are already online " + "(maximum is %d)\n", + ERROR+MAX_SESSIONS_EXCEEDED, + config.c_nodename, config.c_maxsessions); } else { - cprintf("%d %s Citadel/UX server ready.\n",OK,config.c_nodename); + cprintf("%d %s Citadel/UX server ready.\n", + OK, config.c_nodename); } - lprintf(3, "citserver[%3d]: started.\n", CC->cs_pid); + lprintf(3, "citserver[%3d]: started.\n", con->cs_pid); /* Run any session startup routines registered by loadable modules */ PerformSessionHooks(EVT_START); rec_log(CL_CONNECT, ""); +} + + + - do { - time(&CC->lastcmd); - memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ - if (client_gets(cmdbuf) < 1) cleanup(EXIT_NULL); - lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf); - - /* - * Let other clients see the last command we executed, and - * update the idle time, but not NOOP, PEXP, or GEXP. - */ - if ( (strncasecmp(cmdbuf, "NOOP", 4)) - && (strncasecmp(cmdbuf, "PEXP", 4)) - && (strncasecmp(cmdbuf, "GEXP", 4)) ) { - strcpy(CC->lastcmdname, " "); - safestrncpy(CC->lastcmdname, cmdbuf, - sizeof(CC->lastcmdname) ); - time(&CC->lastidle); - } - - if ((strncasecmp(cmdbuf, "ENT0", 4)) && (strncasecmp(cmdbuf, "MESG", 4)) && (strncasecmp(cmdbuf, "MSGS", 4))) - { - CC->cs_flags &= ~CS_POSTING; - } - /* * This loop recognizes all server commands. */ +void do_command_loop(void) { + char cmdbuf[256]; - if (!strncasecmp(cmdbuf,"NOOP",4)) { - cprintf("%d%cok\n",OK,check_express()); - } + lprintf(9, "Hi, I'm do_command_loop() and I have client socket %d\n", + CC->client_socket); - else if (!strncasecmp(cmdbuf,"QUIT",4)) { - cprintf("%d Goodbye.\n",OK); - } + time(&CC->lastcmd); + memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ + if (client_gets(cmdbuf) < 1) { + lprintf(3, "Socket is broken, I think.\n"); + cleanup(EXIT_NULL); + } + lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf); - else if (!strncasecmp(cmdbuf,"LOUT",4)) { - if (CC->logged_in) logout(CC); - cprintf("%d logged out.\n",OK); - } + /* + * Let other clients see the last command we executed, and + * update the idle time, but not NOOP, PEXP, or GEXP. + */ + if ( (strncasecmp(cmdbuf, "NOOP", 4)) + && (strncasecmp(cmdbuf, "PEXP", 4)) + && (strncasecmp(cmdbuf, "GEXP", 4)) ) { + strcpy(CC->lastcmdname, " "); + safestrncpy(CC->lastcmdname, cmdbuf, + sizeof(CC->lastcmdname) ); + time(&CC->lastidle); + } + + if ((strncasecmp(cmdbuf, "ENT0", 4)) + && (strncasecmp(cmdbuf, "MESG", 4)) + && (strncasecmp(cmdbuf, "MSGS", 4))) + { + CC->cs_flags &= ~CS_POSTING; + } + + if (!strncasecmp(cmdbuf,"NOOP",4)) { + cprintf("%d%cok\n",OK,check_express()); + } - else if (!strncasecmp(cmdbuf,"USER",4)) { - cmd_user(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"QUIT",4)) { + cprintf("%d Goodbye.\n",OK); + } - else if (!strncasecmp(cmdbuf,"PASS",4)) { - cmd_pass(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LOUT",4)) { + if (CC->logged_in) logout(CC); + cprintf("%d logged out.\n",OK); + } - else if (!strncasecmp(cmdbuf,"NEWU",4)) { - cmd_newu(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"USER",4)) { + cmd_user(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"SETP",4)) { - cmd_setp(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"PASS",4)) { + cmd_pass(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LRMS",4)) { - cmd_lrms(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"NEWU",4)) { + cmd_newu(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LKRA",4)) { - cmd_lkra(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"SETP",4)) { + cmd_setp(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LKRN",4)) { - cmd_lkrn(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LRMS",4)) { + cmd_lrms(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LKRO",4)) { - cmd_lkro(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LKRA",4)) { + cmd_lkra(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LZRM",4)) { - cmd_lzrm(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LKRN",4)) { + cmd_lkrn(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"GETU",4)) { - cmd_getu(); - } + else if (!strncasecmp(cmdbuf,"LKRO",4)) { + cmd_lkro(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"SETU",4)) { - cmd_setu(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LZRM",4)) { + cmd_lzrm(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"GOTO",4)) { - cmd_goto(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"GETU",4)) { + cmd_getu(); + } - else if (!strncasecmp(cmdbuf,"MSGS",4)) { - cmd_msgs(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"SETU",4)) { + cmd_setu(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"WHOK",4)) { - cmd_whok(); - } + else if (!strncasecmp(cmdbuf,"GOTO",4)) { + cmd_goto(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"RDIR",4)) { - cmd_rdir(); - } + else if (!strncasecmp(cmdbuf,"MSGS",4)) { + cmd_msgs(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"MSG0",4)) { - cmd_msg0(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"WHOK",4)) { + cmd_whok(); + } - else if (!strncasecmp(cmdbuf,"MSG2",4)) { - cmd_msg2(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"RDIR",4)) { + cmd_rdir(); + } - else if (!strncasecmp(cmdbuf,"MSG3",4)) { - cmd_msg3(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"MSG0",4)) { + cmd_msg0(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"MSG4",4)) { - cmd_msg4(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"MSG2",4)) { + cmd_msg2(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"OPNA",4)) { - cmd_opna(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"MSG3",4)) { + cmd_msg3(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"INFO",4)) { - cmd_info(); - } + else if (!strncasecmp(cmdbuf,"MSG4",4)) { + cmd_msg4(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"SLRP",4)) { - cmd_slrp(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"OPNA",4)) { + cmd_opna(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"INVT",4)) { - cmd_invt_kick(&cmdbuf[5],1); - } + else if (!strncasecmp(cmdbuf,"INFO",4)) { + cmd_info(); + } - else if (!strncasecmp(cmdbuf,"KICK",4)) { - cmd_invt_kick(&cmdbuf[5],0); - } + else if (!strncasecmp(cmdbuf,"SLRP",4)) { + cmd_slrp(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"GETR",4)) { - cmd_getr(); - } + else if (!strncasecmp(cmdbuf,"INVT",4)) { + cmd_invt_kick(&cmdbuf[5],1); + } - else if (!strncasecmp(cmdbuf,"SETR",4)) { - cmd_setr(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"KICK",4)) { + cmd_invt_kick(&cmdbuf[5],0); + } - else if (!strncasecmp(cmdbuf,"GETA",4)) { - cmd_geta(); - } + else if (!strncasecmp(cmdbuf,"GETR",4)) { + cmd_getr(); + } - else if (!strncasecmp(cmdbuf,"SETA",4)) { - cmd_seta(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"SETR",4)) { + cmd_setr(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"ENT0",4)) { - cmd_ent0(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"GETA",4)) { + cmd_geta(); + } - else if (!strncasecmp(cmdbuf,"ENT3",4)) { - cmd_ent3(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"SETA",4)) { + cmd_seta(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"RINF",4)) { - cmd_rinf(); - } + else if (!strncasecmp(cmdbuf,"ENT0",4)) { + cmd_ent0(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"DELE",4)) { - cmd_dele(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"ENT3",4)) { + cmd_ent3(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"KILL",4)) { - cmd_kill(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"RINF",4)) { + cmd_rinf(); + } - else if (!strncasecmp(cmdbuf,"CRE8",4)) { - cmd_cre8(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"DELE",4)) { + cmd_dele(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"MOVE",4)) { - cmd_move(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"KILL",4)) { + cmd_kill(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"FORG",4)) { - cmd_forg(); - } + else if (!strncasecmp(cmdbuf,"CRE8",4)) { + cmd_cre8(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"MESG",4)) { - cmd_mesg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"MOVE",4)) { + cmd_move(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"EMSG",4)) { - cmd_emsg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"FORG",4)) { + cmd_forg(); + } - else if (!strncasecmp(cmdbuf,"GNUR",4)) { - cmd_gnur(); - } + else if (!strncasecmp(cmdbuf,"MESG",4)) { + cmd_mesg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"VALI",4)) { - cmd_vali(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"EMSG",4)) { + cmd_emsg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"EINF",4)) { - cmd_einf(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"GNUR",4)) { + cmd_gnur(); + } - else if (!strncasecmp(cmdbuf,"LIST",4)) { - cmd_list(); - } + else if (!strncasecmp(cmdbuf,"VALI",4)) { + cmd_vali(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"CHEK",4)) { - cmd_chek(); - } + else if (!strncasecmp(cmdbuf,"EINF",4)) { + cmd_einf(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"DELF",4)) { - cmd_delf(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LIST",4)) { + cmd_list(); + } - else if (!strncasecmp(cmdbuf,"MOVF",4)) { - cmd_movf(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"CHEK",4)) { + cmd_chek(); + } - else if (!strncasecmp(cmdbuf,"NETF",4)) { - cmd_netf(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"DELF",4)) { + cmd_delf(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"RWHO",4)) { - cmd_rwho(); - } + else if (!strncasecmp(cmdbuf,"MOVF",4)) { + cmd_movf(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"OPEN",4)) { - cmd_open(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"NETF",4)) { + cmd_netf(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"CLOS",4)) { - cmd_clos(); - } + else if (!strncasecmp(cmdbuf,"RWHO",4)) { + cmd_rwho(); + } - else if (!strncasecmp(cmdbuf,"UOPN",4)) { - cmd_uopn(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"OPEN",4)) { + cmd_open(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"UCLS",4)) { - cmd_ucls(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"CLOS",4)) { + cmd_clos(); + } - else if (!strncasecmp(cmdbuf,"READ",4)) { - cmd_read(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"UOPN",4)) { + cmd_uopn(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"WRIT",4)) { - cmd_writ(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"UCLS",4)) { + cmd_ucls(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"QUSR",4)) { - cmd_qusr(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"READ",4)) { + cmd_read(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"ECHO",4)) { - cmd_echo(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"WRIT",4)) { + cmd_writ(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"OIMG",4)) { - cmd_oimg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"QUSR",4)) { + cmd_qusr(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"MORE",4)) { - cmd_more(); - } + else if (!strncasecmp(cmdbuf,"ECHO",4)) { + cmd_echo(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"NETP",4)) { - cmd_netp(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"OIMG",4)) { + cmd_oimg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"NDOP",4)) { - cmd_ndop(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"MORE",4)) { + cmd_more(); + } - else if (!strncasecmp(cmdbuf,"NUOP",4)) { - cmd_nuop(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"NETP",4)) { + cmd_netp(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LFLR",4)) { - cmd_lflr(); - } + else if (!strncasecmp(cmdbuf,"NDOP",4)) { + cmd_ndop(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"CFLR",4)) { - cmd_cflr(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"NUOP",4)) { + cmd_nuop(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"KFLR",4)) { - cmd_kflr(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LFLR",4)) { + cmd_lflr(); + } - else if (!strncasecmp(cmdbuf,"EFLR",4)) { - cmd_eflr(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"CFLR",4)) { + cmd_cflr(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"IDEN",4)) { - cmd_iden(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"KFLR",4)) { + cmd_kflr(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"IPGM",4)) { - cmd_ipgm(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"EFLR",4)) { + cmd_eflr(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"EBIO",4)) { - cmd_ebio(); - } + else if (!strncasecmp(cmdbuf,"IDEN",4)) { + cmd_iden(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"RBIO",4)) { - cmd_rbio(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"IPGM",4)) { + cmd_ipgm(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"LBIO",4)) { - cmd_lbio(); - } + else if (!strncasecmp(cmdbuf,"EBIO",4)) { + cmd_ebio(); + } - else if (!strncasecmp(cmdbuf,"STEL",4)) { - cmd_stel(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"RBIO",4)) { + cmd_rbio(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"TERM",4)) { - cmd_term(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"LBIO",4)) { + cmd_lbio(); + } - else if (!strncasecmp(cmdbuf,"DOWN",4)) { - cmd_down(); - } + else if (!strncasecmp(cmdbuf,"STEL",4)) { + cmd_stel(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf,"SCDN",4)) { - cmd_scdn(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"TERM",4)) { + cmd_term(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "NSET", 4)) { - cmd_nset(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"DOWN",4)) { + cmd_down(); + } - else if (!strncasecmp(cmdbuf, "UIMG", 4)) { - cmd_uimg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf,"SCDN",4)) { + cmd_scdn(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "UCHG", 4)) { - cmd_uchg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "NSET", 4)) { + cmd_nset(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "TIME", 4)) { - cmd_time(); - } + else if (!strncasecmp(cmdbuf, "UIMG", 4)) { + cmd_uimg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "HCHG", 4)) { - cmd_hchg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "UCHG", 4)) { + cmd_uchg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "RCHG", 4)) { - cmd_rchg(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "TIME", 4)) { + cmd_time(); + } - else if (!strncasecmp(cmdbuf, "AGUP", 4)) { - cmd_agup(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "HCHG", 4)) { + cmd_hchg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "ASUP", 4)) { - cmd_asup(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "RCHG", 4)) { + cmd_rchg(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "GPEX", 4)) { - cmd_gpex(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "AGUP", 4)) { + cmd_agup(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "SPEX", 4)) { - cmd_spex(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "ASUP", 4)) { + cmd_asup(&cmdbuf[5]); + } - else if (!strncasecmp(cmdbuf, "CONF", 4)) { - cmd_conf(&cmdbuf[5]); - } + else if (!strncasecmp(cmdbuf, "GPEX", 4)) { + cmd_gpex(&cmdbuf[5]); + } -#ifdef DEBUG_MEMORY_LEAKS - else if (!strncasecmp(cmdbuf, "LEAK", 4)) { - dump_tracked(); - } -#endif + else if (!strncasecmp(cmdbuf, "SPEX", 4)) { + cmd_spex(&cmdbuf[5]); + } - else if (!DLoader_Exec_Cmd(cmdbuf)) - { - cprintf("%d Unrecognized or unsupported command.\n", - ERROR); - } + else if (!strncasecmp(cmdbuf, "CONF", 4)) { + cmd_conf(&cmdbuf[5]); + } - /* Run any after-each-command outines registered by modules */ - PerformSessionHooks(EVT_CMD); +#ifdef DEBUG_MEMORY_LEAKS + else if (!strncasecmp(cmdbuf, "LEAK", 4)) { + dump_tracked(); + } +#endif - } while(strncasecmp(cmdbuf, "QUIT", 4)); + else if (!DLoader_Exec_Cmd(cmdbuf)) + { + cprintf("%d Unrecognized or unsupported command.\n", + ERROR); + } - cleanup(EXIT_NORMAL); - return(NULL); - } + /* Run any after-each-command outines registered by modules */ + PerformSessionHooks(EVT_CMD); +} diff --git a/citadel/citserver.h b/citadel/citserver.h index 53a7801f2..45ac8d519 100644 --- a/citadel/citserver.h +++ b/citadel/citserver.h @@ -1,7 +1,7 @@ /* $Id$ */ void master_startup (void); void master_cleanup (void); -void cleanup_stuff (void *arg); +void cleanup (int); void set_wtmpsupp (char *newtext); void set_wtmpsupp_to_current_room(void); void cmd_info (void); @@ -22,12 +22,13 @@ void cmd_ipgm (char *argbuf); void cmd_down (void); void cmd_scdn (char *argbuf); void cmd_extn (char *argbuf); -void *context_loop (struct CitContext *con); void deallocate_user_data(struct CitContext *con); void *CtdlGetUserData(unsigned long requested_sym); void CtdlAllocUserData(unsigned long requested_sym, size_t num_bytes); int CtdlGetDynamicSymbol(void); void enter_housekeeping_cmd(char *); +void do_command_loop(void); +void begin_session(struct CitContext *con); void GenerateRoomDisplay(char *real_room, struct CitContext *viewed, struct CitContext *viewer); diff --git a/citadel/server.h b/citadel/server.h index 2f977a9d9..b693c275d 100644 --- a/citadel/server.h +++ b/citadel/server.h @@ -56,6 +56,8 @@ struct CitContext { struct usersupp usersupp; /* Database record buffers */ struct quickroom quickroom; + int state; /* thread state (see CON_ values below) */ + char curr_user[32]; /* name of current user */ int logged_in; /* logged in */ int internal_pgm; /* authenticated as internal program */ @@ -63,8 +65,6 @@ struct CitContext { int nologin; /* not allowed to log in */ char net_node[32]; - THREAD mythread; - int n_crit; /* number of critical sections open */ int client_socket; int cs_pid; /* session ID */ time_t cs_lastupdt; /* time of last update */ @@ -103,6 +103,14 @@ struct CitContext { typedef struct CitContext t_context; +/* Values for CitContext.state */ +enum { + CON_IDLE, /* This context is doing nothing */ + CON_EXECUTING, /* This context is bound to a thread */ + CON_DYING /* This context is being terminated */ +}; + + #define CS_STEALTH 1 /* stealth mode */ #define CS_CHAT 2 /* chat mode */ #define CS_POSTING 4 /* Posting */ @@ -153,6 +161,7 @@ enum { S_DATABASE, S_NETDB, S_SUPPMSGMAIN, + S_I_WANNA_SELECT, MAX_SEMAPHORES }; diff --git a/citadel/sysconfig.h b/citadel/sysconfig.h index 200fc27ea..bda55ed28 100644 --- a/citadel/sysconfig.h +++ b/citadel/sysconfig.h @@ -62,6 +62,12 @@ */ #define HOUSEKEEPING_WAKEUP 60 +/* + * We'll almost certainly want to do this more elegantly. For now we are + * creating a fixed-size pool of worker threads. + */ +#define NUM_WORKER_THREADS 15 + /*** STRUCTURE SIZE VARIABLES ***/ diff --git a/citadel/sysdep.c b/citadel/sysdep.c index ea92e4772..0f004f172 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -70,7 +70,7 @@ int msock; /* master listening socket */ int verbosity = 9; /* Logging level */ struct CitContext masterCC; - +int rescan[2]; /* The Rescan Pipe */ /* * lprintf() ... Write logging information @@ -244,72 +244,16 @@ void init_sysdep(void) { */ void begin_critical_section(int which_one) { -#ifdef HAVE_PTHREAD_CANCEL - int oldval; -#else - sigset_t set; -#endif - - /* lprintf(8, "begin_critical_section(%d)\n", which_one); */ - - if (!pthread_equal(pthread_self(), main_thread_id)) { - /* Keep a count of how many critical sections this thread has - * open, so that end_critical_section() doesn't enable - * cancellation prematurely. */ - if (CC != NULL) CC->n_crit++; -#ifdef HAVE_PTHREAD_CANCEL - /* Don't get interrupted during the critical section */ - pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldval); -#else - /* We're faking cancellation with signals. Block SIGUSR1 while - * we're in the critical section. */ - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_BLOCK, &set, NULL); -#endif - } - - /* Obtain a semaphore */ pthread_mutex_lock(&Critters[which_one]); - - } +} /* * Release a semaphore lock to end a critical section. */ void end_critical_section(int which_one) { -#ifdef HAVE_PTHREAD_CANCEL - int oldval; -#else - sigset_t set; -#endif - - /* lprintf(8, " end_critical_section(%d)\n", which_one); */ - - /* Let go of the semaphore */ pthread_mutex_unlock(&Critters[which_one]); - - if (!pthread_equal(pthread_self(), main_thread_id)) - if (CC != NULL) - if (!--CC->n_crit) { -#ifdef HAVE_PTHREAD_CANCEL - /* If a cancel was sent during the critical section, do it now. - * Then re-enable thread cancellation. - */ - pthread_testcancel(); - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldval); - pthread_testcancel(); -#else - /* We're faking it. Unblock SIGUSR1; signals sent during the - * critical section should now be able to kill us. */ - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); -#endif - } - - } +} @@ -359,26 +303,6 @@ int ig_tcp_server(int port_number, int queue_len) } -/* - * Return a pointer to a thread's own CitContext structure (old) - * NOTE: this version of MyContext() is commented out because it is no longer - * in use. It was written before I discovered TSD keys. This - * version pounds through the context list until it finds the one matching - * the currently running thread. It remains here, commented out, in case it - * is needed for future ports to threading libraries which have the equivalent - * of pthread_self() but not pthread_key_create() and its ilk. - * - * struct CitContext *MyContext() { - * struct CitContext *ptr; - * THREAD me; - * - * me = pthread_self(); - * for (ptr=ContextList; ptr!=NULL; ptr=ptr->next) { - * if (ptr->mythread == me) return(ptr); - * } - * return(NULL); - * } - */ /* * Return a pointer to a thread's own CitContext structure (new) @@ -400,10 +324,16 @@ struct CitContext *CreateNewContext(void) { me = (struct CitContext *) mallok(sizeof(struct CitContext)); if (me == NULL) { lprintf(1, "citserver: can't allocate memory!!\n"); - pthread_exit(NULL); + return NULL; } memset(me, 0, sizeof(struct CitContext)); + /* The new context will be created already in the CON_EXECUTING state + * in order to prevent another thread from grabbing it while it's + * being set up. + */ + me->state = CON_EXECUTING; + begin_critical_section(S_SESSION_TABLE); me->next = ContextList; ContextList = me; @@ -411,26 +341,6 @@ struct CitContext *CreateNewContext(void) { return(me); } -/* - * Add a thread's thread ID to the context - */ -void InitMyContext(struct CitContext *con) -{ -#ifdef HAVE_PTHREAD_CANCEL - int oldval; -#endif - - con->mythread = pthread_self(); -#ifdef HAVE_PTHREAD_CANCEL - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldval); - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldval); -#endif - if (pthread_setspecific(MyConKey, (void *)con) != 0) { - lprintf(1, "ERROR! pthread_setspecific() failed: %s\n", - strerror(errno)); - } - } - /* * Remove a context from the context list. */ @@ -555,11 +465,12 @@ int client_read_to(char *buf, int bytes, int timeout) while(lenclient_socket, &rfds); - tv.tv_sec = timeout; + tv.tv_sec = 1; tv.tv_usec = 0; retval = select( (CC->client_socket)+1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(CC->client_socket, &rfds) == 0) { return(0); } @@ -627,60 +538,23 @@ void sysdep_master_cleanup(void) { close(msock); } -/* - * Cleanup routine to be called when one thread is shutting down. - */ -void cleanup(int exit_code) -{ - /* Terminate the thread. - * Its cleanup handler will call cleanup_stuff() - */ - lprintf(7, "Calling pthread_exit()\n"); - pthread_exit(NULL); - } /* * Terminate another session. */ void kill_session(int session_to_kill) { struct CitContext *ptr; - THREAD killme = 0; begin_critical_section(S_SESSION_TABLE); for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { if (ptr->cs_pid == session_to_kill) { - killme = ptr->mythread; + ptr->state = CON_DYING; } } end_critical_section(S_SESSION_TABLE); - - if (killme != 0) { -#ifdef HAVE_PTHREAD_CANCEL - pthread_cancel(killme); -#else - pthread_kill(killme, SIGUSR1); -#ifdef __FreeBSD__ - /* there's a very stupid bug in the user threads package on - FreeBSD 3.1 which prevents a signal from being properly - dispatched to a thread that's in a blocking syscall. the - first signal interrupts the syscall, the second one actually - gets delivered. */ - pthread_kill(killme, SIGUSR1); -#endif -#endif - } } -/* - * The system-dependent wrapper around the main context loop. - */ -void *sd_context_loop(struct CitContext *con) { - pthread_cleanup_push(*cleanup_stuff, NULL); - context_loop(con); - pthread_cleanup_pop(0); - return NULL; - } /* @@ -798,17 +672,10 @@ int convert_login(char NameToConvert[]) { */ int main(int argc, char **argv) { - struct sockaddr_in fsin; /* Data for master socket */ - int alen; /* Data for master socket */ - int ssock; /* Descriptor for master socket */ - THREAD SessThread; /* Thread descriptor */ THREAD HousekeepingThread; /* Thread descriptor */ pthread_attr_t attr; /* Thread attributes */ - struct CitContext *con; /* Temporary context pointer */ char tracefile[128]; /* Name of file to log traces to */ int a, i; /* General-purpose variables */ - fd_set readfds; - struct timeval tv; struct passwd *pw; int drop_root_perms = 1; char *moddir; @@ -934,55 +801,203 @@ int main(int argc, char **argv) strerror(errno)); } - /* - * Endless loop. Listen on the master socket. When a connection - * comes in, create a socket, a context, and a thread. - */ + + /* + * 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(1, "Can't create rescan pipe!\n"); + exit(errno); + } + + /* + * Now create a bunch of worker threads. + */ + for (i=0; i<(NUM_WORKER_THREADS-1); ++i) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (pthread_create(&HousekeepingThread, &attr, + (void* (*)(void*)) worker_thread, NULL) != 0) { + lprintf(1, "Can't create worker thead: %s\n", + strerror(errno)); + } + } + + /* Now this thread can become a worker as well. */ + worker_thread(); + + return(0); +} + + + + + + + +/* + * This loop just keeps going and going and going... + */ +void worker_thread(void) { + int i; + char junk; + int numselect = 0; + int highest; + struct CitContext *ptr; + struct CitContext *bind_me = NULL; + fd_set readfds; + int retval; + struct CitContext *con= NULL; /* Temporary context pointer */ + struct sockaddr_in fsin; /* Data for master socket */ + int alen; /* Data for master socket */ + int ssock; /* Descriptor for master socket */ + while (!time_to_die) { - /* we need to check if a signal has been delivered. because - * syscalls may be restartable across signals, we call - * select with a timeout of 1 second and repeatedly check for - * time_to_die... */ + + /* + * In a stupid environment, we would have all idle threads + * calling select() and then they'd all wake up at once. We + * solve this problem by putting the select() in a critical + * section, so only one thread has the opportunity to wake + * up. If we wake up on the master socket, create a new + * session context; otherwise, just bind the thread to the + * context we want and go on our merry way. + */ + + begin_critical_section(S_I_WANNA_SELECT); + lprintf(9, "Worker thread %2d woke up\n", getpid()); + +SETUP_FD: FD_ZERO(&readfds); FD_SET(msock, &readfds); - tv.tv_sec = 1; - tv.tv_usec = 0; - if (select(msock + 1, &readfds, NULL, NULL, &tv) <= 0) - continue; - alen = sizeof fsin; - ssock = accept(msock, (struct sockaddr *)&fsin, &alen); - if (ssock < 0) { - lprintf(2, "citserver: accept() failed: %s\n", - strerror(errno)); + highest = msock; + FD_SET(rescan[0], &readfds); + if (rescan[0] > highest) highest = rescan[0]; + numselect = 2; + + begin_critical_section(S_SESSION_TABLE); + for (ptr = ContextList; ptr != NULL; ptr = ptr->next) { + if (ptr->state == CON_IDLE) { + FD_SET(ptr->client_socket, &readfds); + if (ptr->client_socket > highest) + highest = ptr->client_socket; + ++numselect; } - else { - lprintf(7, "citserver: Client socket %d\n", ssock); - con = CreateNewContext(); - con->client_socket = ssock; - - /* Set the SO_REUSEADDR socket option */ - i = 1; - setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, - &i, sizeof(i)); - - /* set attributes for the new thread */ - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, - PTHREAD_CREATE_DETACHED); - - /* now create the thread */ - if (pthread_create(&SessThread, &attr, - (void* (*)(void*)) sd_context_loop, - con) - != 0) { - lprintf(1, - "citserver: can't create thread: %s\n", + } + end_critical_section(S_SESSION_TABLE); + + lprintf(9, "Thread %2d can wake up on %d different fd's\n", + getpid(), numselect); + retval = select(highest + 1, &readfds, NULL, NULL, NULL); + lprintf(9, "select() returned %d\n", retval); + + /* Now figure out who made this select() unblock. + * First, check for an error or exit condition. + */ + if (retval < 0) { + end_critical_section(S_I_WANNA_SELECT); + lprintf(9, "Exiting (%s)\n", errno); + time_to_die = 1; + } + + /* Next, check to see if it's a new client connecting + * on the master socket. + */ + else if (FD_ISSET(msock, &readfds)) { + lprintf(9, "It's the master socket!\n"); + alen = sizeof fsin; + ssock = accept(msock, (struct sockaddr *)&fsin, &alen); + if (ssock < 0) { + lprintf(2, "citserver: accept() failed: %s\n", strerror(errno)); + } + else { + lprintf(7, "citserver: New client socket %d\n", + ssock); + + /* New context will be created already set up + * in the CON_EXECUTING state. + */ + con = CreateNewContext(); + + /* Assign our new socket number to it. */ + con->client_socket = ssock; + + /* Set the SO_REUSEADDR socket option */ + i = 1; + setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, + &i, sizeof(i)); + + pthread_setspecific(MyConKey, (void *)con); + begin_session(con); + /* do_command_loop(); */ + pthread_setspecific(MyConKey, (void *)NULL); + con->state = CON_IDLE; + goto SETUP_FD; + } + } + + /* If the rescan pipe went active, someone is telling this + * thread that the &readfds needs to be refreshed with more + * current data. + */ + else if (FD_ISSET(rescan[0], &readfds)) { + lprintf(9, "rescanning\n"); + 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. + */ + else { + bind_me = NULL; + begin_critical_section(S_SESSION_TABLE); + for (ptr = ContextList; + ( (ptr != NULL) && (bind_me == NULL) ); + ptr = ptr->next) { + if ( (FD_ISSET(ptr->client_socket, &readfds)) + && (ptr->state == CON_IDLE) ) { + bind_me = con; } + } + if (bind_me != NULL) { + /* Found one. Stake a claim to it before + * letting anyone else touch the context list. + */ + bind_me->state = CON_EXECUTING; + } + 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) { + lprintf(9, "Binding thread to session %d\n", + bind_me->client_socket); + pthread_setspecific(MyConKey, (void *)bind_me); + do_command_loop(); + pthread_setspecific(MyConKey, (void *)NULL); + if (bind_me->state == CON_DYING) { + RemoveContext(bind_me); + } + else { + bind_me->state = CON_IDLE; + } + write(rescan[1], &junk, 1); + } + else { + lprintf(9, "Thread %02d found nothing to do!\n", + getpid()); } + } - master_cleanup(); - return 0; + } + pthread_exit(NULL); +} + diff --git a/citadel/sysdep_decls.h b/citadel/sysdep_decls.h index 4f1587f8c..ef5b9e700 100644 --- a/citadel/sysdep_decls.h +++ b/citadel/sysdep_decls.h @@ -15,9 +15,9 @@ int client_read_to (char *buf, int bytes, int timeout); int client_read (char *buf, int bytes); int client_gets (char *buf); void sysdep_master_cleanup (void); -void cleanup (int exit_code); void kill_session (int session_to_kill); void *sd_context_loop (struct CitContext *con); void start_daemon (int do_close_stdio); void cmd_nset (char *cmdbuf); int convert_login (char *NameToConvert); +void worker_thread (void);