diff options
Diffstat (limited to 'test/time-sem.c')
-rw-r--r-- | test/time-sem.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/test/time-sem.c b/test/time-sem.c new file mode 100644 index 0000000..7bd0501 --- /dev/null +++ b/test/time-sem.c @@ -0,0 +1,591 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +time-sem.c has the basics of the semaphores we use in http_main.c. It's +intended for timing differences between various methods on an +architecture. In practice we've found many things affect which semaphore +to be used: + + - NFS filesystems absolutely suck for fcntl() and flock() + + - uslock absolutely sucks on single-processor IRIX boxes, but + absolutely rocks on multi-processor boxes. The converse + is true for fcntl. sysvsem seems a moderate balance. + + - Under Solaris you can't have too many processes use SEM_UNDO, there + might be a tuneable somewhere that increases the limit from 29. + We're not sure what the tunable is, so there's a define + NO_SEM_UNDO which can be used to simulate us trapping/blocking + signals to be able to properly release the semaphore on a clean + child death. You'll also need to define NEED_UNION_SEMUN + under solaris. + +You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap() +doesn't work on your system (i.e. linux). + +argv[1] is the #children, argv[2] is the #iterations per child + +You should run each over many different #children inputs, and choose +#iter such that the program runs for at least a second or so... or even +longer depending on your patience. + +compile with: + +gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT +gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT +gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT +gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO +gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread +gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT + +not all versions work on all systems. +*/ + +#include <errno.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <signal.h> + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) + +static struct flock lock_it; +static struct flock unlock_it; + +static int fcntl_fd=-1; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void +accept_mutex_init(void) +{ + + 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 */ + + printf("opening test-lock-thing in current directory\n"); + fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644); + if (fcntl_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing"); + exit (1); + } + unlink("test-lock-thing"); +} + +void accept_mutex_on(void) +{ + int ret; + + while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror ("fcntl lock_it"); + exit(1); + } +} + +void accept_mutex_off(void) +{ + if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0) + { + perror ("fcntl unlock_it"); + exit(1); + } +} + +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +#include <sys/file.h> + +static int flock_fd=-1; + +#define FNAME "test-lock-thing" + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void accept_mutex_init(void) +{ + + printf("opening " FNAME " in current directory\n"); + flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (flock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing"); + exit (1); + } +} + +void accept_mutex_child_init(void) +{ + flock_fd = open(FNAME, O_WRONLY, 0600); + if (flock_fd == -1) { + perror("open"); + exit(1); + } +} + +void accept_mutex_cleanup(void) +{ + unlink(FNAME); +} + +void accept_mutex_on(void) +{ + int ret; + + while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror ("flock(LOCK_EX)"); + exit(1); + } +} + +void accept_mutex_off(void) +{ + if (flock (flock_fd, LOCK_UN) < 0) + { + perror ("flock(LOCK_UN)"); + exit(1); + } +} + +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT) + +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> + +static int sem_id = -1; +#ifdef NO_SEM_UNDO +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; +#endif + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ +#ifdef NEED_UNION_SEMUN + /* believe it or not, you need to define this under solaris */ + union semun { + int val; + struct semid_ds *buf; + ushort *array; + }; +#endif + + union semun ick; + + sem_id = semget(999, 1, IPC_CREAT | 0666); + if (sem_id < 0) { + perror ("semget"); + exit (1); + } + ick.val = 1; + if (semctl(sem_id, 0, SETVAL, ick) < 0) { + perror ("semctl"); + exit(1); + } +#ifdef NO_SEM_UNDO + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); +#endif +} + +void accept_mutex_on() +{ + struct sembuf op; + +#ifdef NO_SEM_UNDO + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + exit (1); + } + op.sem_flg = 0; +#else + op.sem_flg = SEM_UNDO; +#endif + op.sem_num = 0; + op.sem_op = -1; + if (semop(sem_id, &op, 1) < 0) { + perror ("accept_mutex_on"); + exit (1); + } +} + +void accept_mutex_off() +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 1; +#ifdef NO_SEM_UNDO + op.sem_flg = 0; +#else + op.sem_flg = SEM_UNDO; +#endif + if (semop(sem_id, &op, 1) < 0) { + perror ("accept_mutex_off"); + exit (1); + } +#ifdef NO_SEM_UNDO + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + exit (1); + } +#endif +} + +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT) + +/* note: pthread mutexes aren't released on child death, hence the + * signal goop ... in a real implementation we'd do special things + * during hup, term, usr1. + */ + +#include <pthread.h> + +static pthread_mutex_t *mutex; +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ + pthread_mutexattr_t mattr; + int fd; + + fd = open ("/dev/zero", O_RDWR); + if (fd == -1) { + perror ("open(/dev/zero)"); + exit (1); + } + mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex), + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (mutex == (void *)(caddr_t)-1) { + perror ("mmap"); + exit (1); + } + close (fd); + if (pthread_mutexattr_init(&mattr)) { + perror ("pthread_mutexattr_init"); + exit (1); + } + if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) { + perror ("pthread_mutexattr_setpshared"); + exit (1); + } + if (pthread_mutex_init(mutex, &mattr)) { + perror ("pthread_mutex_init"); + exit (1); + } + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); +} + +void accept_mutex_on() +{ + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + exit (1); + } + if (pthread_mutex_lock (mutex)) { + perror ("pthread_mutex_lock"); + exit (1); + } +} + +void accept_mutex_off() +{ + if (pthread_mutex_unlock (mutex)) { + perror ("pthread_mutex_unlock"); + exit (1); + } + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + exit (1); + } +} + +#elif defined (USE_USLOCK_SERIALIZED_ACCEPT) + +#include <ulocks.h> + +static usptr_t *us = NULL; +static ulock_t uslock = NULL; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ + ptrdiff_t old; + /* default is 8 */ +#define CONF_INITUSERS_MAX 15 + if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((us = usinit("/dev/zero")) == NULL) { + perror("usinit"); + exit(-1); + } + if ((uslock = usnewlock(us)) == NULL) { + perror("usnewlock"); + exit(-1); + } +} +void accept_mutex_on() +{ + switch(ussetlock(uslock)) { + case 1: + /* got lock */ + break; + case 0: + fprintf(stderr, "didn't get lock\n"); + exit(-1); + case -1: + perror("ussetlock"); + exit(-1); + } +} +void accept_mutex_off() +{ + if (usunsetlock(uslock) == -1) { + perror("usunsetlock"); + exit(-1); + } +} +#endif + + +#ifndef USE_SHMGET_SCOREBOARD +static void *get_shared_mem(apr_size_t size) +{ + void *result; + + /* allocate shared memory for the shared_counter */ + result = (unsigned long *)mmap ((caddr_t)0, size, + PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); + if (result == (void *)(caddr_t)-1) { + perror ("mmap"); + exit (1); + } + return result; +} +#else +#include <sys/types.h> +#include <sys/ipc.h> +#ifdef HAVE_SYS_MUTEX_H +#include <sys/mutex.h> +#endif +#include <sys/shm.h> + +static void *get_shared_mem(apr_size_t size) +{ + key_t shmkey = IPC_PRIVATE; + int shmid = -1; + void *result; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) { + perror("shmget"); + exit(1); + } + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { + perror("sbrk"); + } +#endif + +#define BADSHMAT ((void *)(-1)) + if ((result = shmat(shmid, 0, 0)) == BADSHMAT) { + perror("shmat"); + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + perror("shmctl(IPC_RMID)"); + } + if (result == BADSHMAT) /* now bailout */ + exit(1); + +#ifdef MOVEBREAK + if (obrk == (char *) -1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *) -1) { + perror("sbrk 2"); + } +#endif + return result; +} +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING +/* don't ask */ +#define _P __P +#include <sched.h> +#define YIELD sched_yield() +#else +#define YIELD do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0) +#endif + +void main (int argc, char **argv) +{ + int num_iter; + int num_child; + int i; + struct timeval first; + struct timeval last; + long ms; + int pid; + unsigned long *shared_counter; + + if (argc != 3) { + fprintf (stderr, "Usage: time-sem num-child num iter\n"); + exit (1); + } + + num_child = atoi (argv[1]); + num_iter = atoi (argv[2]); + + /* allocate shared memory for the shared_counter */ + shared_counter = get_shared_mem(sizeof(*shared_counter)); + + /* initialize counter to 0 */ + *shared_counter = 0; + + accept_mutex_init (); + + /* parent grabs mutex until done spawning children */ + accept_mutex_on (); + + for (i = 0; i < num_child; ++i) { + pid = fork(); + if (pid == 0) { + /* child, do our thing */ + accept_mutex_child_init(); + for (i = 0; i < num_iter; ++i) { + unsigned long tmp; + + accept_mutex_on (); + tmp = *shared_counter; + YIELD; + *shared_counter = tmp + 1; + accept_mutex_off (); + } + exit (0); + } else if (pid == -1) { + perror ("fork"); + exit (1); + } + } + + /* a quick test to see that nothing is screwed up */ + if (*shared_counter != 0) { + puts ("WTF! shared_counter != 0 before the children have been started!"); + exit (1); + } + + gettimeofday (&first, NULL); + /* launch children into action */ + accept_mutex_off (); + for (i = 0; i < num_child; ++i) { + if (wait(NULL) == -1) { + perror ("wait"); + } + } + gettimeofday (&last, NULL); + + if (*shared_counter != num_child * num_iter) { + printf ("WTF! shared_counter != num_child * num_iter!\n" + "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n", + *shared_counter, + num_child, num_iter); + } + + last.tv_sec -= first.tv_sec; + ms = last.tv_usec - first.tv_usec; + if (ms < 0) { + --last.tv_sec; + ms += 1000000; + } + last.tv_usec = ms; + printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec); + + accept_mutex_cleanup(); + + exit(0); +} + |