1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 time-sem.c has the basics of the semaphores we use in http_main.c. It's
19 intended for timing differences between various methods on an
20 architecture. In practice we've found many things affect which semaphore
23 - NFS filesystems absolutely suck for fcntl() and flock()
25 - uslock absolutely sucks on single-processor IRIX boxes, but
26 absolutely rocks on multi-processor boxes. The converse
27 is true for fcntl. sysvsem seems a moderate balance.
29 - Under Solaris you can't have too many processes use SEM_UNDO, there
30 might be a tuneable somewhere that increases the limit from 29.
31 We're not sure what the tunable is, so there's a define
32 NO_SEM_UNDO which can be used to simulate us trapping/blocking
33 signals to be able to properly release the semaphore on a clean
34 child death. You'll also need to define NEED_UNION_SEMUN
37 You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap()
38 doesn't work on your system (i.e. linux).
40 argv[1] is the #children, argv[2] is the #iterations per child
42 You should run each over many different #children inputs, and choose
43 #iter such that the program runs for at least a second or so... or even
44 longer depending on your patience.
48 gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT
49 gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT
50 gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT
51 gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO
52 gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread
53 gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT
55 not all versions work on all systems.
64 #include <sys/types.h>
71 #if defined(USE_FCNTL_SERIALIZED_ACCEPT)
73 static struct flock lock_it;
74 static struct flock unlock_it;
76 static int fcntl_fd=-1;
78 #define accept_mutex_child_init()
79 #define accept_mutex_cleanup()
82 * Initialize mutex lock.
83 * Must be safe to call this on a restart.
86 accept_mutex_init(void)
89 lock_it.l_whence = SEEK_SET; /* from current point */
90 lock_it.l_start = 0; /* -"- */
91 lock_it.l_len = 0; /* until end of file */
92 lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
93 lock_it.l_pid = 0; /* pid not actually interesting */
94 unlock_it.l_whence = SEEK_SET; /* from current point */
95 unlock_it.l_start = 0; /* -"- */
96 unlock_it.l_len = 0; /* until end of file */
97 unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
98 unlock_it.l_pid = 0; /* pid not actually interesting */
100 printf("opening test-lock-thing in current directory\n");
101 fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644);
105 fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
108 unlink("test-lock-thing");
111 void accept_mutex_on(void)
115 while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR)
119 perror ("fcntl lock_it");
124 void accept_mutex_off(void)
126 if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0)
128 perror ("fcntl unlock_it");
133 #elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
135 #include <sys/file.h>
137 static int flock_fd=-1;
139 #define FNAME "test-lock-thing"
142 * Initialize mutex lock.
143 * Must be safe to call this on a restart.
145 void accept_mutex_init(void)
148 printf("opening " FNAME " in current directory\n");
149 flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644);
153 fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
158 void accept_mutex_child_init(void)
160 flock_fd = open(FNAME, O_WRONLY, 0600);
161 if (flock_fd == -1) {
167 void accept_mutex_cleanup(void)
172 void accept_mutex_on(void)
176 while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
180 perror ("flock(LOCK_EX)");
185 void accept_mutex_off(void)
187 if (flock (flock_fd, LOCK_UN) < 0)
189 perror ("flock(LOCK_UN)");
194 #elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
196 #include <sys/types.h>
200 static int sem_id = -1;
202 static sigset_t accept_block_mask;
203 static sigset_t accept_previous_mask;
206 #define accept_mutex_child_init()
207 #define accept_mutex_cleanup()
209 void accept_mutex_init(void)
211 #ifdef NEED_UNION_SEMUN
212 /* believe it or not, you need to define this under solaris */
215 struct semid_ds *buf;
222 sem_id = semget(999, 1, IPC_CREAT | 0666);
228 if (semctl(sem_id, 0, SETVAL, ick) < 0) {
233 sigfillset(&accept_block_mask);
234 sigdelset(&accept_block_mask, SIGHUP);
235 sigdelset(&accept_block_mask, SIGTERM);
236 sigdelset(&accept_block_mask, SIGUSR1);
240 void accept_mutex_on()
245 if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
246 perror("sigprocmask(SIG_BLOCK)");
251 op.sem_flg = SEM_UNDO;
255 if (semop(sem_id, &op, 1) < 0) {
256 perror ("accept_mutex_on");
261 void accept_mutex_off()
270 op.sem_flg = SEM_UNDO;
272 if (semop(sem_id, &op, 1) < 0) {
273 perror ("accept_mutex_off");
277 if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
278 perror("sigprocmask(SIG_SETMASK)");
284 #elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
286 /* note: pthread mutexes aren't released on child death, hence the
287 * signal goop ... in a real implementation we'd do special things
288 * during hup, term, usr1.
293 static pthread_mutex_t *mutex;
294 static sigset_t accept_block_mask;
295 static sigset_t accept_previous_mask;
297 #define accept_mutex_child_init()
298 #define accept_mutex_cleanup()
300 void accept_mutex_init(void)
302 pthread_mutexattr_t mattr;
305 fd = open ("/dev/zero", O_RDWR);
307 perror ("open(/dev/zero)");
310 mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex),
311 PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
312 if (mutex == (void *)(caddr_t)-1) {
317 if (pthread_mutexattr_init(&mattr)) {
318 perror ("pthread_mutexattr_init");
321 if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) {
322 perror ("pthread_mutexattr_setpshared");
325 if (pthread_mutex_init(mutex, &mattr)) {
326 perror ("pthread_mutex_init");
329 sigfillset(&accept_block_mask);
330 sigdelset(&accept_block_mask, SIGHUP);
331 sigdelset(&accept_block_mask, SIGTERM);
332 sigdelset(&accept_block_mask, SIGUSR1);
335 void accept_mutex_on()
337 if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
338 perror("sigprocmask(SIG_BLOCK)");
341 if (pthread_mutex_lock (mutex)) {
342 perror ("pthread_mutex_lock");
347 void accept_mutex_off()
349 if (pthread_mutex_unlock (mutex)) {
350 perror ("pthread_mutex_unlock");
353 if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
354 perror("sigprocmask(SIG_SETMASK)");
359 #elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
363 static usptr_t *us = NULL;
364 static ulock_t uslock = NULL;
366 #define accept_mutex_child_init()
367 #define accept_mutex_cleanup()
369 void accept_mutex_init(void)
373 #define CONF_INITUSERS_MAX 15
374 if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) {
378 if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
382 if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
386 if ((us = usinit("/dev/zero")) == NULL) {
390 if ((uslock = usnewlock(us)) == NULL) {
395 void accept_mutex_on()
397 switch(ussetlock(uslock)) {
402 fprintf(stderr, "didn't get lock\n");
409 void accept_mutex_off()
411 if (usunsetlock(uslock) == -1) {
412 perror("usunsetlock");
419 #ifndef USE_SHMGET_SCOREBOARD
420 static void *get_shared_mem(apr_size_t size)
424 /* allocate shared memory for the shared_counter */
425 result = (unsigned long *)mmap ((caddr_t)0, size,
426 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
427 if (result == (void *)(caddr_t)-1) {
434 #include <sys/types.h>
436 #ifdef HAVE_SYS_MUTEX_H
437 #include <sys/mutex.h>
441 static void *get_shared_mem(apr_size_t size)
443 key_t shmkey = IPC_PRIVATE;
450 if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) {
457 * Some SysV systems place the shared segment WAY too close
458 * to the dynamic memory break point (sbrk(0)). This severely
459 * limits the use of malloc/sbrk in the program since sbrk will
460 * refuse to move past that point.
462 * To get around this, we move the break point "way up there",
463 * attach the segment and then move break back down. Ugly
465 if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
470 #define BADSHMAT ((void *)(-1))
471 if ((result = shmat(shmid, 0, 0)) == BADSHMAT) {
475 * We must avoid leaving segments in the kernel's
478 if (shmctl(shmid, IPC_RMID, NULL) != 0) {
479 perror("shmctl(IPC_RMID)");
481 if (result == BADSHMAT) /* now bailout */
485 if (obrk == (char *) -1)
486 return; /* nothing else to do */
487 if (sbrk(-(MOVEBREAK)) == (char *) -1) {
495 #ifdef _POSIX_PRIORITY_SCHEDULING
499 #define YIELD sched_yield()
501 #define YIELD do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0)
504 void main (int argc, char **argv)
509 struct timeval first;
513 unsigned long *shared_counter;
516 fprintf (stderr, "Usage: time-sem num-child num iter\n");
520 num_child = atoi (argv[1]);
521 num_iter = atoi (argv[2]);
523 /* allocate shared memory for the shared_counter */
524 shared_counter = get_shared_mem(sizeof(*shared_counter));
526 /* initialize counter to 0 */
529 accept_mutex_init ();
531 /* parent grabs mutex until done spawning children */
534 for (i = 0; i < num_child; ++i) {
537 /* child, do our thing */
538 accept_mutex_child_init();
539 for (i = 0; i < num_iter; ++i) {
543 tmp = *shared_counter;
545 *shared_counter = tmp + 1;
549 } else if (pid == -1) {
555 /* a quick test to see that nothing is screwed up */
556 if (*shared_counter != 0) {
557 puts ("WTF! shared_counter != 0 before the children have been started!");
561 gettimeofday (&first, NULL);
562 /* launch children into action */
564 for (i = 0; i < num_child; ++i) {
565 if (wait(NULL) == -1) {
569 gettimeofday (&last, NULL);
571 if (*shared_counter != num_child * num_iter) {
572 printf ("WTF! shared_counter != num_child * num_iter!\n"
573 "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n",
575 num_child, num_iter);
578 last.tv_sec -= first.tv_sec;
579 ms = last.tv_usec - first.tv_usec;
585 printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec);
587 accept_mutex_cleanup();