time-sem.c revision b8d55029467b9f139d081cf38c6d06ff45e742a9
/* Copyright 2000-2004 Apache Software Foundation
*
* Licensed 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
*
*
* 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
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 <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
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)
{
printf("opening test-lock-thing in current directory\n");
if (fcntl_fd == -1)
{
perror ("open");
exit (1);
}
unlink("test-lock-thing");
}
void accept_mutex_on(void)
{
int ret;
continue;
if (ret < 0) {
perror ("fcntl lock_it");
exit(1);
}
}
void accept_mutex_off(void)
{
{
perror ("fcntl unlock_it");
exit(1);
}
}
#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
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)
{
if (flock_fd == -1)
{
perror ("open");
exit (1);
}
}
void accept_mutex_child_init(void)
{
if (flock_fd == -1) {
perror("open");
exit(1);
}
}
void accept_mutex_cleanup(void)
{
}
void accept_mutex_on(void)
{
int ret;
continue;
if (ret < 0) {
perror ("flock(LOCK_EX)");
exit(1);
}
}
void accept_mutex_off(void)
{
{
perror ("flock(LOCK_UN)");
exit(1);
}
}
#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
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;
};
#endif
if (sem_id < 0) {
perror ("semget");
exit (1);
}
perror ("semctl");
exit(1);
}
#ifdef NO_SEM_UNDO
#endif
}
void accept_mutex_on()
{
#ifdef NO_SEM_UNDO
perror("sigprocmask(SIG_BLOCK)");
exit (1);
}
#else
#endif
perror ("accept_mutex_on");
exit (1);
}
}
void accept_mutex_off()
{
#ifdef NO_SEM_UNDO
#else
#endif
perror ("accept_mutex_off");
exit (1);
}
#ifdef NO_SEM_UNDO
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)
{
int fd;
if (fd == -1) {
exit (1);
}
perror ("mmap");
exit (1);
}
if (pthread_mutexattr_init(&mattr)) {
perror ("pthread_mutexattr_init");
exit (1);
}
perror ("pthread_mutexattr_setpshared");
exit (1);
}
perror ("pthread_mutex_init");
exit (1);
}
}
void accept_mutex_on()
{
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);
}
perror("sigprocmask(SIG_SETMASK)");
exit (1);
}
}
#elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
#include <ulocks.h>
#define accept_mutex_child_init()
#define accept_mutex_cleanup()
void accept_mutex_init(void)
{
/* default is 8 */
#define CONF_INITUSERS_MAX 15
perror("usconfig");
exit(-1);
}
perror("usconfig");
exit(-1);
}
perror("usconfig");
exit(-1);
}
perror("usinit");
exit(-1);
}
perror("usnewlock");
exit(-1);
}
}
void accept_mutex_on()
{
case 1:
/* got lock */
break;
case 0:
exit(-1);
case -1:
perror("ussetlock");
exit(-1);
}
}
void accept_mutex_off()
{
perror("usunsetlock");
exit(-1);
}
}
#endif
#ifndef USE_SHMGET_SCOREBOARD
{
void *result;
/* allocate shared memory for the shared_counter */
perror ("mmap");
exit (1);
}
return result;
}
#else
#ifdef HAVE_SYS_MUTEX_H
#endif
{
int shmid = -1;
void *result;
#ifdef MOVEBREAK
char *obrk;
#endif
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
* 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
*/
perror("sbrk");
}
#endif
#define BADSHMAT ((void *)(-1))
perror("shmat");
}
/*
* We must avoid leaving segments in the kernel's
* (small) tables.
*/
perror("shmctl(IPC_RMID)");
}
exit(1);
#ifdef MOVEBREAK
if (obrk == (char *) -1)
return; /* nothing else to do */
perror("sbrk 2");
}
#endif
return result;
}
#endif
#ifdef _POSIX_PRIORITY_SCHEDULING
/* don't ask */
#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
{
int num_iter;
int num_child;
int i;
long ms;
int pid;
unsigned long *shared_counter;
if (argc != 3) {
exit (1);
}
/* allocate shared memory for the shared_counter */
/* initialize counter to 0 */
*shared_counter = 0;
/* parent grabs mutex until done spawning children */
accept_mutex_on ();
for (i = 0; i < num_child; ++i) {
if (pid == 0) {
/* child, do our thing */
for (i = 0; i < num_iter; ++i) {
unsigned long tmp;
accept_mutex_on ();
tmp = *shared_counter;
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);
}
/* launch children into action */
accept_mutex_off ();
for (i = 0; i < num_child; ++i) {
perror ("wait");
}
}
printf ("WTF! shared_counter != num_child * num_iter!\n"
"shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n",
}
if (ms < 0) {
ms += 1000000;
}
exit(0);
}