]> granicus.if.org Git - apache/blob - test/time-sem.c
mod_proxy: Fix ProxyRemoteMatch directive.
[apache] / test / time-sem.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /*
17 time-sem.c has the basics of the semaphores we use in http_main.c.  It's
18 intended for timing differences between various methods on an
19 architecture.  In practice we've found many things affect which semaphore
20 to be used:
21
22     - NFS filesystems absolutely suck for fcntl() and flock()
23
24     - uslock absolutely sucks on single-processor IRIX boxes, but
25         absolutely rocks on multi-processor boxes.  The converse
26         is true for fcntl.  sysvsem seems a moderate balance.
27
28     - Under Solaris you can't have too many processes use SEM_UNDO, there
29         might be a tuneable somewhere that increases the limit from 29.
30         We're not sure what the tunable is, so there's a define
31         NO_SEM_UNDO which can be used to simulate us trapping/blocking
32         signals to be able to properly release the semaphore on a clean
33         child death.  You'll also need to define NEED_UNION_SEMUN
34         under solaris.
35
36 You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap()
37 doesn't work on your system (i.e. linux).
38
39 argv[1] is the #children, argv[2] is the #iterations per child
40
41 You should run each over many different #children inputs, and choose
42 #iter such that the program runs for at least a second or so... or even
43 longer depending on your patience.
44
45 compile with:
46
47 gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT
48 gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT
49 gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT
50 gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO
51 gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread
52 gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT
53
54 not all versions work on all systems.
55 */
56
57 #include <errno.h>
58 #include <sys/time.h>
59 #include <unistd.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include <stdlib.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <fcntl.h>
66 #include <sys/wait.h>
67 #include <sys/mman.h>
68 #include <signal.h>
69
70 #if defined(USE_FCNTL_SERIALIZED_ACCEPT)
71
72 static struct flock lock_it;
73 static struct flock unlock_it;
74
75 static int fcntl_fd=-1;
76
77 #define accept_mutex_child_init()
78 #define accept_mutex_cleanup()
79
80 /*
81  * Initialize mutex lock.
82  * Must be safe to call this on a restart.
83  */
84 void
85 accept_mutex_init(void)
86 {
87
88     lock_it.l_whence = SEEK_SET;   /* from current point */
89     lock_it.l_start  = 0;          /* -"- */
90     lock_it.l_len    = 0;          /* until end of file */
91     lock_it.l_type   = F_WRLCK;    /* set exclusive/write lock */
92     lock_it.l_pid    = 0;          /* pid not actually interesting */
93     unlock_it.l_whence = SEEK_SET; /* from current point */
94     unlock_it.l_start  = 0;        /* -"- */
95     unlock_it.l_len    = 0;        /* until end of file */
96     unlock_it.l_type   = F_UNLCK;  /* set exclusive/write lock */
97     unlock_it.l_pid    = 0;        /* pid not actually interesting */
98
99     printf("opening test-lock-thing in current directory\n");
100     fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644);
101     if (fcntl_fd == -1)
102     {
103         perror ("open");
104         fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
105         exit (1);
106     }
107     unlink("test-lock-thing");
108 }
109
110 void accept_mutex_on(void)
111 {
112     int ret;
113     
114     while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR)
115         continue;
116
117     if (ret < 0) {
118         perror ("fcntl lock_it");
119         exit(1);
120     }
121 }
122
123 void accept_mutex_off(void)
124 {
125     if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0)
126     {
127         perror ("fcntl unlock_it");
128         exit(1);
129     }
130 }
131
132 #elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
133
134 #include <sys/file.h>
135
136 static int flock_fd=-1;
137
138 #define FNAME "test-lock-thing"
139
140 /*
141  * Initialize mutex lock.
142  * Must be safe to call this on a restart.
143  */
144 void accept_mutex_init(void)
145 {
146
147     printf("opening " FNAME " in current directory\n");
148     flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644);
149     if (flock_fd == -1)
150     {
151         perror ("open");
152         fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
153         exit (1);
154     }
155 }
156
157 void accept_mutex_child_init(void)
158 {
159     flock_fd = open(FNAME, O_WRONLY, 0600);
160     if (flock_fd == -1) {
161         perror("open");
162         exit(1);
163     }
164 }
165
166 void accept_mutex_cleanup(void)
167 {
168     unlink(FNAME);
169 }
170
171 void accept_mutex_on(void)
172 {
173     int ret;
174     
175     while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
176         continue;
177
178     if (ret < 0) {
179         perror ("flock(LOCK_EX)");
180         exit(1);
181     }
182 }
183
184 void accept_mutex_off(void)
185 {
186     if (flock (flock_fd, LOCK_UN) < 0)
187     {
188         perror ("flock(LOCK_UN)");
189         exit(1);
190     }
191 }
192
193 #elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
194
195 #include <sys/types.h>
196 #include <sys/ipc.h>
197 #include <sys/sem.h>
198
199 static   int sem_id = -1;
200 #ifdef NO_SEM_UNDO
201 static sigset_t accept_block_mask;
202 static sigset_t accept_previous_mask;
203 #endif
204
205 #define accept_mutex_child_init()
206 #define accept_mutex_cleanup()
207
208 void accept_mutex_init(void)
209 {
210 #ifdef NEED_UNION_SEMUN
211     /* believe it or not, you need to define this under solaris */
212     union semun {
213         int val;
214         struct semid_ds *buf;
215         ushort *array;
216     };
217 #endif
218
219     union semun ick;
220
221     sem_id = semget(999, 1, IPC_CREAT | 0666);
222     if (sem_id < 0) {
223        perror ("semget");
224        exit (1);
225     }
226     ick.val = 1;
227     if (semctl(sem_id, 0, SETVAL, ick) < 0) {
228        perror ("semctl");
229         exit(1);
230     }
231 #ifdef NO_SEM_UNDO
232     sigfillset(&accept_block_mask);
233     sigdelset(&accept_block_mask, SIGHUP);
234     sigdelset(&accept_block_mask, SIGTERM);
235     sigdelset(&accept_block_mask, SIGUSR1);
236 #endif
237 }
238
239 void accept_mutex_on()
240 {
241     struct sembuf op;
242
243 #ifdef NO_SEM_UNDO
244     if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
245         perror("sigprocmask(SIG_BLOCK)");
246         exit (1);
247     }
248     op.sem_flg = 0;
249 #else
250     op.sem_flg = SEM_UNDO;
251 #endif
252     op.sem_num = 0;
253     op.sem_op  = -1;
254     if (semop(sem_id, &op, 1) < 0) {
255         perror ("accept_mutex_on");
256         exit (1);
257     }
258 }
259
260 void accept_mutex_off()
261 {
262     struct sembuf op;
263
264     op.sem_num = 0;
265     op.sem_op  = 1;
266 #ifdef NO_SEM_UNDO
267     op.sem_flg = 0;
268 #else
269     op.sem_flg = SEM_UNDO;
270 #endif
271     if (semop(sem_id, &op, 1) < 0) {
272         perror ("accept_mutex_off");
273         exit (1);
274     }
275 #ifdef NO_SEM_UNDO
276     if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
277         perror("sigprocmask(SIG_SETMASK)");
278         exit (1);
279     }
280 #endif
281 }
282
283 #elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
284
285 /* note: pthread mutexes aren't released on child death, hence the
286  * signal goop ... in a real implementation we'd do special things
287  * during hup, term, usr1.
288  */
289
290 #include <pthread.h>
291
292 static pthread_mutex_t *mutex;
293 static sigset_t accept_block_mask;
294 static sigset_t accept_previous_mask;
295
296 #define accept_mutex_child_init()
297 #define accept_mutex_cleanup()
298
299 void accept_mutex_init(void)
300 {
301     pthread_mutexattr_t mattr;
302     int fd;
303
304     fd = open ("/dev/zero", O_RDWR);
305     if (fd == -1) {
306         perror ("open(/dev/zero)");
307         exit (1);
308     }
309     mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex),
310                     PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
311     if (mutex == (void *)(caddr_t)-1) {
312         perror ("mmap");
313         exit (1);
314     }
315     close (fd);
316     if (pthread_mutexattr_init(&mattr)) {
317         perror ("pthread_mutexattr_init");
318         exit (1);
319     }
320     if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) {
321         perror ("pthread_mutexattr_setpshared");
322         exit (1);
323     }
324     if (pthread_mutex_init(mutex, &mattr)) {
325         perror ("pthread_mutex_init");
326         exit (1);
327     }
328     sigfillset(&accept_block_mask);
329     sigdelset(&accept_block_mask, SIGHUP);
330     sigdelset(&accept_block_mask, SIGTERM);
331     sigdelset(&accept_block_mask, SIGUSR1);
332 }
333
334 void accept_mutex_on()
335 {
336     if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
337         perror("sigprocmask(SIG_BLOCK)");
338         exit (1);
339     }
340     if (pthread_mutex_lock (mutex)) {
341         perror ("pthread_mutex_lock");
342         exit (1);
343     }
344 }
345
346 void accept_mutex_off()
347 {
348     if (pthread_mutex_unlock (mutex)) {
349         perror ("pthread_mutex_unlock");
350         exit (1);
351     }
352     if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
353         perror("sigprocmask(SIG_SETMASK)");
354         exit (1);
355     }
356 }
357
358 #elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
359
360 #include <ulocks.h>
361
362 static usptr_t *us = NULL;
363 static ulock_t uslock = NULL;
364
365 #define accept_mutex_child_init()
366 #define accept_mutex_cleanup()
367
368 void accept_mutex_init(void)
369 {
370     ptrdiff_t old;
371     /* default is 8 */
372 #define CONF_INITUSERS_MAX 15
373     if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) {
374         perror("usconfig");
375         exit(-1);
376     }
377     if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
378         perror("usconfig");
379         exit(-1);
380     }
381     if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
382         perror("usconfig");
383         exit(-1);
384     }
385     if ((us = usinit("/dev/zero")) == NULL) {
386         perror("usinit");
387         exit(-1);
388     }
389     if ((uslock = usnewlock(us)) == NULL) {
390         perror("usnewlock");
391         exit(-1);
392     }
393 }
394 void accept_mutex_on()
395 {
396     switch(ussetlock(uslock)) {
397         case 1:
398             /* got lock */
399             break;
400         case 0:
401             fprintf(stderr, "didn't get lock\n");
402             exit(-1);
403         case -1:
404             perror("ussetlock");
405             exit(-1);
406     }
407 }
408 void accept_mutex_off()
409 {
410     if (usunsetlock(uslock) == -1) {
411         perror("usunsetlock");
412         exit(-1);
413     }
414 }
415 #endif
416
417
418 #ifndef USE_SHMGET_SCOREBOARD
419 static void *get_shared_mem(apr_size_t size)
420 {
421     void *result;
422
423     /* allocate shared memory for the shared_counter */
424     result = (unsigned long *)mmap ((caddr_t)0, size,
425                     PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
426     if (result == (void *)(caddr_t)-1) {
427         perror ("mmap");
428         exit (1);
429     }
430     return result;
431 }
432 #else
433 #include <sys/types.h>
434 #include <sys/ipc.h>
435 #ifdef HAVE_SYS_MUTEX_H
436 #include <sys/mutex.h>
437 #endif
438 #include <sys/shm.h>
439
440 static void *get_shared_mem(apr_size_t size)
441 {
442     key_t shmkey = IPC_PRIVATE;
443     int shmid = -1;
444     void *result;
445 #ifdef MOVEBREAK
446     char *obrk;
447 #endif
448
449     if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) {
450         perror("shmget");
451         exit(1);
452     }
453
454 #ifdef MOVEBREAK
455     /*
456      * Some SysV systems place the shared segment WAY too close
457      * to the dynamic memory break point (sbrk(0)). This severely
458      * limits the use of malloc/sbrk in the program since sbrk will
459      * refuse to move past that point.
460      *
461      * To get around this, we move the break point "way up there",
462      * attach the segment and then move break back down. Ugly
463      */
464     if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
465         perror("sbrk");
466     }
467 #endif
468
469 #define BADSHMAT        ((void *)(-1))
470     if ((result = shmat(shmid, 0, 0)) == BADSHMAT) {
471         perror("shmat");
472     }
473     /*
474      * We must avoid leaving segments in the kernel's
475      * (small) tables.
476      */
477     if (shmctl(shmid, IPC_RMID, NULL) != 0) {
478         perror("shmctl(IPC_RMID)");
479     }
480     if (result == BADSHMAT)     /* now bailout */
481         exit(1);
482
483 #ifdef MOVEBREAK
484     if (obrk == (char *) -1)
485         return;                 /* nothing else to do */
486     if (sbrk(-(MOVEBREAK)) == (char *) -1) {
487         perror("sbrk 2");
488     }
489 #endif
490     return result;
491 }
492 #endif
493
494 #ifdef _POSIX_PRIORITY_SCHEDULING
495 /* don't ask */
496 #define _P __P
497 #include <sched.h>
498 #define YIELD   sched_yield()
499 #else
500 #define YIELD   do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0)
501 #endif
502
503 void main (int argc, char **argv)
504 {
505     int num_iter;
506     int num_child;
507     int i;
508     struct timeval first;
509     struct timeval last;
510     long ms;
511     int pid;
512     unsigned long *shared_counter;
513
514     if (argc != 3) {
515         fprintf (stderr, "Usage: time-sem num-child num iter\n");
516         exit (1);
517     }
518
519     num_child = atoi (argv[1]);
520     num_iter = atoi (argv[2]);
521
522     /* allocate shared memory for the shared_counter */
523     shared_counter = get_shared_mem(sizeof(*shared_counter));
524
525     /* initialize counter to 0 */
526     *shared_counter = 0;
527
528     accept_mutex_init ();
529
530     /* parent grabs mutex until done spawning children */
531     accept_mutex_on ();
532
533     for (i = 0; i < num_child; ++i) {
534         pid = fork();
535         if (pid == 0) {
536             /* child, do our thing */
537             accept_mutex_child_init();
538             for (i = 0; i < num_iter; ++i) {
539                 unsigned long tmp;
540
541                 accept_mutex_on ();
542                 tmp = *shared_counter;
543                 YIELD;
544                 *shared_counter = tmp + 1;
545                 accept_mutex_off ();
546             }
547             exit (0);
548         } else if (pid == -1) {
549             perror ("fork");
550             exit (1);
551         }
552     }
553
554     /* a quick test to see that nothing is screwed up */
555     if (*shared_counter != 0) {
556         puts ("WTF! shared_counter != 0 before the children have been started!");
557         exit (1);
558     }
559
560     gettimeofday (&first, NULL);
561     /* launch children into action */
562     accept_mutex_off ();
563     for (i = 0; i < num_child; ++i) {
564         if (wait(NULL) == -1) {
565             perror ("wait");
566         }
567     }
568     gettimeofday (&last, NULL);
569
570     if (*shared_counter != num_child * num_iter) {
571         printf ("WTF! shared_counter != num_child * num_iter!\n"
572                 "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n",
573                 *shared_counter,
574                 num_child, num_iter);
575     }
576
577     last.tv_sec -= first.tv_sec;
578     ms = last.tv_usec - first.tv_usec;
579     if (ms < 0) {
580         --last.tv_sec;
581         ms += 1000000;
582     }
583     last.tv_usec = ms;
584     printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec);
585
586     accept_mutex_cleanup();
587
588     exit(0);
589 }
590