rbb         99/02/16 07:34:04

  Modified:    pthreads/src/main http_main.c
  Log:
  First pass at accept loop serialization.  Right now, it is one mutex for the
  whole server.  This should be one mutex per socket, but I just wanted to get
  the basic code in, I'll clean it up later.
  
  Revision  Changes    Path
  1.30      +512 -3    apache-apr/pthreads/src/main/http_main.c
  
  Index: http_main.c
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/main/http_main.c,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- http_main.c       1999/02/16 07:10:27     1.29
  +++ http_main.c       1999/02/16 15:34:04     1.30
  @@ -247,8 +247,6 @@
   }
   #define tls() ((tls_main_t *) gettls(tls_main_key)) /* ZZZZZ */
   
  -
  -/* used by accept_loop(), which is serialized */
   static listen_rec *head_listener;
   
   /* *Non*-shared http_main globals... */
  @@ -427,6 +425,514 @@
       exit(code);
   }
   
  +
  +/******  ZZZ this should probably be abstracted to it's own file.   ****/
  +
  +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || 
defined(USE_FLOCK_SERIALIZED_ACCEPT)
  +static void expand_lock_fname(pool *p)
  +{
  +    /* XXXX possibly bogus cast */
  +    ap_lock_fname = ap_psprintf(p, "%s.%lu",
  +     ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid());
  +}
  +#endif
  +
  +#if defined (USE_USLOCK_SERIALIZED_ACCEPT)
  +
  +#include <ulocks.h>
  +
  +static ulock_t uslock = NULL;
  +
  +#define accept_mutex_child_init(x)
  +
  +static void accept_mutex_init(pool *p)
  +{
  +    ptrdiff_t old;
  +    usptr_t *us;
  +
  +
  +    /* default is 8, allocate enough for all the children plus the parent */
  +    if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) {
  +     perror("usconfig(CONF_INITUSERS)");
  +     exit(-1);
  +    }
  +
  +    if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
  +     perror("usconfig(CONF_LOCKTYPE)");
  +     exit(-1);
  +    }
  +    if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
  +     perror("usconfig(CONF_ARENATYPE)");
  +     exit(-1);
  +    }
  +    if ((us = usinit("/dev/zero")) == NULL) {
  +     perror("usinit");
  +     exit(-1);
  +    }
  +
  +    if ((uslock = usnewlock(us)) == NULL) {
  +     perror("usnewlock");
  +     exit(-1);
  +    }
  +}
  +
  +static void accept_mutex_on(void)
  +{
  +    switch (ussetlock(uslock)) {
  +    case 1:
  +     /* got lock */
  +     break;
  +    case 0:
  +     fprintf(stderr, "didn't get lock\n");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    case -1:
  +     perror("ussetlock");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +static void accept_mutex_off(void)
  +{
  +    if (usunsetlock(uslock) == -1) {
  +     perror("usunsetlock");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
  +
  +/* This code probably only works on Solaris ... but it works really fast
  + * on Solaris.  Note that pthread mutexes are *NOT* released when a task
  + * dies ... the task has to free it itself.  So we block signals and
  + * try to be nice about releasing the mutex.
  + */
  +
  +#include <pthread.h>
  +
  +static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1;
  +static int have_accept_mutex;
  +static sigset_t accept_block_mask;
  +static sigset_t accept_previous_mask;
  +
  +static void accept_mutex_child_cleanup(void *foo)
  +{
  +    if (accept_mutex != (void *)(caddr_t)-1
  +     && have_accept_mutex) {
  +     pthread_mutex_unlock(accept_mutex);
  +    }
  +}
  +
  +static void accept_mutex_child_init(pool *p)
  +{
  +    ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, 
ap_null_cleanup);
  +}
  +
  +static void accept_mutex_cleanup(void *foo)
  +{
  +    if (accept_mutex != (void *)(caddr_t)-1
  +     && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) {
  +     perror("munmap");
  +    }
  +    accept_mutex = (void *)(caddr_t)-1;
  +}
  +
  +static void accept_mutex_init(pool *p)
  +{
  +    pthread_mutexattr_t mattr;
  +    int fd;
  +
  +    fd = open("/dev/zero", O_RDWR);
  +    if (fd == -1) {
  +     perror("open(/dev/zero)");
  +     exit(APEXIT_INIT);
  +    }
  +    accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, 
sizeof(*accept_mutex),
  +                              PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  +    if (accept_mutex == (void *) (caddr_t) - 1) {
  +     perror("mmap");
  +     exit(APEXIT_INIT);
  +    }
  +    close(fd);
  +    if ((errno = pthread_mutexattr_init(&mattr))) {
  +     perror("pthread_mutexattr_init");
  +     exit(APEXIT_INIT);
  +    }
  +    if ((errno = pthread_mutexattr_setpshared(&mattr,
  +                                             PTHREAD_PROCESS_SHARED))) {
  +     perror("pthread_mutexattr_setpshared");
  +     exit(APEXIT_INIT);
  +    }
  +    if ((errno = pthread_mutex_init(accept_mutex, &mattr))) {
  +     perror("pthread_mutex_init");
  +     exit(APEXIT_INIT);
  +    }
  +    sigfillset(&accept_block_mask);
  +    sigdelset(&accept_block_mask, SIGHUP);
  +    sigdelset(&accept_block_mask, SIGTERM);
  +    sigdelset(&accept_block_mask, SIGUSR1);
  +    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
  +}
  +
  +static void accept_mutex_on(void)
  +{
  +    int err;
  +
  +    if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
  +     perror("sigprocmask(SIG_BLOCK)");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +    if ((err = pthread_mutex_lock(accept_mutex))) {
  +     errno = err;
  +     perror("pthread_mutex_lock");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +    have_accept_mutex = 1;
  +}
  +
  +static void accept_mutex_off(void)
  +{
  +    int err;
  +
  +    if ((err = pthread_mutex_unlock(accept_mutex))) {
  +     errno = err;
  +     perror("pthread_mutex_unlock");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +    /* There is a slight race condition right here... if we were to die right
  +     * now, we'd do another pthread_mutex_unlock.  Now, doing that would let
  +     * another process into the mutex.  pthread mutexes are designed to be
  +     * fast, as such they don't have protection for things like testing if 
the
  +     * thread owning a mutex is actually unlocking it (or even any way of
  +     * testing who owns the mutex).
  +     *
  +     * If we were to unset have_accept_mutex prior to releasing the mutex
  +     * then the race could result in the server unable to serve hits.  Doing
  +     * it this way means that the server can continue, but an additional
  +     * child might be in the critical section ... at least it's still serving
  +     * hits.
  +     */
  +    have_accept_mutex = 0;
  +    if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
  +     perror("sigprocmask(SIG_SETMASK)");
  +     clean_child_exit(1);
  +    }
  +}
  +
  +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
  +
  +#include <sys/types.h>
  +#include <sys/ipc.h>
  +#include <sys/sem.h>
  +
  +#ifdef NEED_UNION_SEMUN
  +/* it makes no sense, but this isn't defined on solaris */
  +union semun {
  +    long val;
  +    struct semid_ds *buf;
  +    ushort *array;
  +};
  +
  +#endif
  +
  +static int sem_id = -1;
  +static struct sembuf op_on;
  +static struct sembuf op_off;
  +
  +/* We get a random semaphore ... the lame sysv semaphore interface
  + * means we have to be sure to clean this up or else we'll leak
  + * semaphores.
  + */
  +static void accept_mutex_cleanup(void *foo)
  +{
  +    union semun ick;
  +
  +    if (sem_id < 0)
  +     return;
  +    /* this is ignored anyhow */
  +    ick.val = 0;
  +    semctl(sem_id, 0, IPC_RMID, ick);
  +}
  +
  +#define accept_mutex_child_init(x)
  +
  +static void accept_mutex_init(pool *p)
  +{
  +    union semun ick;
  +    struct semid_ds buf;
  +
  +    /* acquire the semaphore */
  +    sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
  +    if (sem_id < 0) {
  +     perror("semget");
  +     exit(APEXIT_INIT);
  +    }
  +    ick.val = 1;
  +    if (semctl(sem_id, 0, SETVAL, ick) < 0) {
  +     perror("semctl(SETVAL)");
  +     exit(APEXIT_INIT);
  +    }
  +    if (!getuid()) {
  +     /* restrict it to use only by the appropriate user_id ... not that this
  +      * stops CGIs from acquiring it and dinking around with it.
  +      */
  +     buf.sem_perm.uid = ap_user_id;
  +     buf.sem_perm.gid = ap_group_id;
  +     buf.sem_perm.mode = 0600;
  +     ick.buf = &buf;
  +     if (semctl(sem_id, 0, IPC_SET, ick) < 0) {
  +         perror("semctl(IPC_SET)");
  +         exit(APEXIT_INIT);
  +     }
  +    }
  +    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
  +
  +    /* pre-initialize these */
  +    op_on.sem_num = 0;
  +    op_on.sem_op = -1;
  +    op_on.sem_flg = SEM_UNDO;
  +    op_off.sem_num = 0;
  +    op_off.sem_op = 1;
  +    op_off.sem_flg = SEM_UNDO;
  +}
  +
  +static void accept_mutex_on(void)
  +{
  +    if (semop(sem_id, &op_on, 1) < 0) {
  +     perror("accept_mutex_on");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +static void accept_mutex_off(void)
  +{
  +    if (semop(sem_id, &op_off, 1) < 0) {
  +     perror("accept_mutex_off");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +#elif defined(USE_FCNTL_SERIALIZED_ACCEPT)
  +static struct flock lock_it;
  +static struct flock unlock_it;
  +
  +static int lock_fd = -1;
  +
  +#define accept_mutex_child_init(x)
  +
  +/*
  + * Initialize mutex lock.
  + * Must be safe to call this on a restart.
  + */
  +static void accept_mutex_init(pool *p)
  +{
  +
  +    lock_it.l_whence = SEEK_SET;     /* from current point */
  +    lock_it.l_start = 0;             /* -"- */
  +    lock_it.l_len = 0;                       /* until end of file */
  +    lock_it.l_type = F_WRLCK;                /* set exclusive/write lock */
  +    lock_it.l_pid = 0;                       /* pid not actually interesting 
*/
  +    unlock_it.l_whence = SEEK_SET;   /* from current point */
  +    unlock_it.l_start = 0;           /* -"- */
  +    unlock_it.l_len = 0;             /* until end of file */
  +    unlock_it.l_type = F_UNLCK;              /* set exclusive/write lock */
  +    unlock_it.l_pid = 0;             /* pid not actually interesting */
  +
  +    expand_lock_fname(p);
  +    lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644);
  +    if (lock_fd == -1) {
  +     perror("open");
  +     fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname);
  +     exit(APEXIT_INIT);
  +    }
  +    unlink(ap_lock_fname);
  +}
  +
  +static void accept_mutex_on(void)
  +{
  +    int ret;
  +
  +    while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) 
{
  +     /* nop */
  +    }
  +
  +    if (ret < 0) {
  +     ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
  +                 "fcntl: F_SETLKW: Error getting accept lock, exiting!  "
  +                 "Perhaps you need to use the LockFile directive to place "
  +                 "your lock file on a local disk!");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +static void accept_mutex_off(void)
  +{
  +    int ret;
  +
  +    while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == 
EINTR) {
  +     /* nop */
  +    }
  +    if (ret < 0) {
  +     ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
  +                 "fcntl: F_SETLKW: Error freeing accept lock, exiting!  "
  +                 "Perhaps you need to use the LockFile directive to place "
  +                 "your lock file on a local disk!");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
  +
  +static int lock_fd = -1;
  +
  +static void accept_mutex_cleanup(void *foo)
  +{
  +    unlink(ap_lock_fname);
  +}
  +
  +/*
  + * Initialize mutex lock.
  + * Done by each child at it's birth
  + */
  +static void accept_mutex_child_init(pool *p)
  +{
  +
  +    lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600);
  +    if (lock_fd == -1) {
  +     ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
  +                 "Child cannot open lock file: %s", ap_lock_fname);
  +     clean_child_exit(APEXIT_CHILDINIT);
  +    }
  +}
  +
  +/*
  + * Initialize mutex lock.
  + * Must be safe to call this on a restart.
  + */
  +static void accept_mutex_init(pool *p)
  +{
  +    expand_lock_fname(p);
  +    unlink(ap_lock_fname);
  +    lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600);
  +    if (lock_fd == -1) {
  +     ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
  +                 "Parent cannot open lock file: %s", ap_lock_fname);
  +     exit(APEXIT_INIT);
  +    }
  +    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
  +}
  +
  +static void accept_mutex_on(void)
  +{
  +    int ret;
  +
  +    while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR)
  +     continue;
  +
  +    if (ret < 0) {
  +     ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
  +                 "flock: LOCK_EX: Error getting accept lock. Exiting!");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +static void accept_mutex_off(void)
  +{
  +    if (flock(lock_fd, LOCK_UN) < 0) {
  +     ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
  +                 "flock: LOCK_UN: Error freeing accept lock. Exiting!");
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT)
  +
  +static HMTX lock_sem = -1;
  +
  +static void accept_mutex_cleanup(void *foo)
  +{
  +    DosReleaseMutexSem(lock_sem);
  +    DosCloseMutexSem(lock_sem);
  +}
  +
  +/*
  + * Initialize mutex lock.
  + * Done by each child at it's birth
  + */
  +static void accept_mutex_child_init(pool *p)
  +{
  +    int rc = DosOpenMutexSem(NULL, &lock_sem);
  +
  +    if (rc != 0) {
  +     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
  +                 "Child cannot open lock semaphore, rc=%d", rc);
  +     clean_child_exit(APEXIT_CHILDINIT);
  +    }
  +}
  +
  +/*
  + * Initialize mutex lock.
  + * Must be safe to call this on a restart.
  + */
  +static void accept_mutex_init(pool *p)
  +{
  +    int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE);
  +
  +    if (rc != 0) {
  +     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
  +                 "Parent cannot create lock semaphore, rc=%d", rc);
  +     exit(APEXIT_INIT);
  +    }
  +
  +    ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
  +}
  +
  +static void accept_mutex_on(void)
  +{
  +    int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT);
  +
  +    if (rc != 0) {
  +     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
  +                 "OS2SEM: Error %d getting accept lock. Exiting!", rc);
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +static void accept_mutex_off(void)
  +{
  +    int rc = DosReleaseMutexSem(lock_sem);
  +    
  +    if (rc != 0) {
  +     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
  +                 "OS2SEM: Error %d freeing accept lock. Exiting!", rc);
  +     clean_child_exit(APEXIT_CHILDFATAL);
  +    }
  +}
  +
  +#else
  +/* Default --- no serialization.  Other methods *could* go here,
  + * as #elifs...
  + */
  +#if !defined(MULTITHREAD)
  +/* Multithreaded systems don't complete between processes for
  + * the sockets. */
  +#define NO_SERIALIZED_ACCEPT
  +#define accept_mutex_child_init(x)
  +#define accept_mutex_init(x)
  +#define accept_mutex_on()
  +#define accept_mutex_off()
  +#endif
  +#endif
  +
  +/*** End of accept serialization code.   */
  +
  +/* On some architectures it's safe to do unserialized accept()s in the single
  + * Listen case.  But it's never safe to do it in the case where there's
  + * multiple Listen statements.  Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
  + * when it's safe in the single Listen case.  We haven't defined this yet
  + * for the hybrid server. ZZZ
  + */
  +#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
  +
   static void usage(char *bin)
   {
       char pad[MAX_STRING_LEN];
  @@ -1768,7 +2274,10 @@
       while (0 < requests_this_child) {
           (void) ap_update_child_status(my_pid, my_tid, SERVER_ACCEPTING, 
                                    (request_rec *) NULL);
  +     /* lock around the accept if necessary */
  +     SAFE_ACCEPT(accept_mutex_on());
           csd = accept(sd, &sa_client, &len);
  +     SAFE_ACCEPT(accept_mutex_off());
        (void) ap_update_child_status(my_pid, my_tid, SERVER_QUEUEING, 
                                      (request_rec *) NULL);
        if (csd >= 0) {
  @@ -1955,7 +2464,7 @@
   
       /*stuff to do before we switch id's, so we have permissions.*/
       reopen_scoreboard(pchild);
  -    /*    SAFE_ACCEPT(accept_mutex_child_init(pchild));*/
  +    SAFE_ACCEPT(accept_mutex_child_init(pchild));
   
       set_group_privs();
   
  
  
  

Reply via email to