bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / test / time-sem.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
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
21 to be used:
22
23     - NFS filesystems absolutely suck for fcntl() and flock()
24
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.
28
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
35         under solaris.
36
37 You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap()
38 doesn't work on your system (i.e. linux).
39
40 argv[1] is the #children, argv[2] is the #iterations per child
41
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.
45
46 compile with:
47
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
54
55 not all versions work on all systems.
56 */
57
58 #include <errno.h>
59 #include <sys/time.h>
60 #include <unistd.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <fcntl.h>
67 #include <sys/wait.h>
68 #include <sys/mman.h>
69 #include <signal.h>
70
71 #if defined(USE_FCNTL_SERIALIZED_ACCEPT)
72
73 static struct flock lock_it;
74 static struct flock unlock_it;
75
76 static int fcntl_fd=-1;
77
78 #define accept_mutex_child_init()
79 #define accept_mutex_cleanup()
80
81 /*
82  * Initialize mutex lock.
83  * Must be safe to call this on a restart.
84  */
85 void
86 accept_mutex_init(void)
87 {
88
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 */
99
100     printf("opening test-lock-thing in current directory\n");
101     fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644);
102     if (fcntl_fd == -1)
103     {
104         perror ("open");
105         fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
106         exit (1);
107     }
108     unlink("test-lock-thing");
109 }
110
111 void accept_mutex_on(void)
112 {
113     int ret;
114     
115     while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR)
116         continue;
117
118     if (ret < 0) {
119         perror ("fcntl lock_it");
120         exit(1);
121     }
122 }
123
124 void accept_mutex_off(void)
125 {
126     if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0)
127     {
128         perror ("fcntl unlock_it");
129         exit(1);
130     }
131 }
132
133 #elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
134
135 #include <sys/file.h>
136
137 static int flock_fd=-1;
138
139 #define FNAME "test-lock-thing"
140
141 /*
142  * Initialize mutex lock.
143  * Must be safe to call this on a restart.
144  */
145 void accept_mutex_init(void)
146 {
147
148     printf("opening " FNAME " in current directory\n");
149     flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644);
150     if (flock_fd == -1)
151     {
152         perror ("open");
153         fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
154         exit (1);
155     }
156 }
157
158 void accept_mutex_child_init(void)
159 {
160     flock_fd = open(FNAME, O_WRONLY, 0600);
161     if (flock_fd == -1) {
162         perror("open");
163         exit(1);
164     }
165 }
166
167 void accept_mutex_cleanup(void)
168 {
169     unlink(FNAME);
170 }
171
172 void accept_mutex_on(void)
173 {
174     int ret;
175     
176     while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
177         continue;
178
179     if (ret < 0) {
180         perror ("flock(LOCK_EX)");
181         exit(1);
182     }
183 }
184
185 void accept_mutex_off(void)
186 {
187     if (flock (flock_fd, LOCK_UN) < 0)
188     {
189         perror ("flock(LOCK_UN)");
190         exit(1);
191     }
192 }
193
194 #elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
195
196 #include <sys/types.h>
197 #include <sys/ipc.h>
198 #include <sys/sem.h>
199
200 static   int sem_id = -1;
201 #ifdef NO_SEM_UNDO
202 static sigset_t accept_block_mask;
203 static sigset_t accept_previous_mask;
204 #endif
205
206 #define accept_mutex_child_init()
207 #define accept_mutex_cleanup()
208
209 void accept_mutex_init(void)
210 {
211 #ifdef NEED_UNION_SEMUN
212     /* believe it or not, you need to define this under solaris */
213     union semun {
214         int val;
215         struct semid_ds *buf;
216         ushort *array;
217     };
218 #endif
219
220     union semun ick;
221
222     sem_id = semget(999, 1, IPC_CREAT | 0666);
223     if (sem_id < 0) {
224        perror ("semget");
225        exit (1);
226     }
227     ick.val = 1;
228     if (semctl(sem_id, 0, SETVAL, ick) < 0) {
229        perror ("semctl");
230         exit(1);
231     }
232 #ifdef NO_SEM_UNDO
233     sigfillset(&accept_block_mask);
234     sigdelset(&accept_block_mask, SIGHUP);
235     sigdelset(&accept_block_mask, SIGTERM);
236     sigdelset(&accept_block_mask, SIGUSR1);
237 #endif
238 }
239
240 void accept_mutex_on()
241 {
242     struct sembuf op;
243
244 #ifdef NO_SEM_UNDO
245     if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
246         perror("sigprocmask(SIG_BLOCK)");
247         exit (1);
248     }
249     op.sem_flg = 0;
250 #else
251     op.sem_flg = SEM_UNDO;
252 #endif
253     op.sem_num = 0;
254     op.sem_op  = -1;
255     if (semop(sem_id, &op, 1) < 0) {
256         perror ("accept_mutex_on");
257         exit (1);
258     }
259 }
260
261 void accept_mutex_off()
262 {
263     struct sembuf op;
264
265     op.sem_num = 0;
266     op.sem_op  = 1;
267 #ifdef NO_SEM_UNDO
268     op.sem_flg = 0;
269 #else
270     op.sem_flg = SEM_UNDO;
271 #endif
272     if (semop(sem_id, &op, 1) < 0) {
273         perror ("accept_mutex_off");
274         exit (1);
275     }
276 #ifdef NO_SEM_UNDO
277     if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
278         perror("sigprocmask(SIG_SETMASK)");
279         exit (1);
280     }
281 #endif
282 }
283
284 #elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
285
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.
289  */
290
291 #include <pthread.h>
292
293 static pthread_mutex_t *mutex;
294 static sigset_t accept_block_mask;
295 static sigset_t accept_previous_mask;
296
297 #define accept_mutex_child_init()
298 #define accept_mutex_cleanup()
299
300 void accept_mutex_init(void)
301 {
302     pthread_mutexattr_t mattr;
303     int fd;
304
305     fd = open ("/dev/zero", O_RDWR);
306     if (fd == -1) {
307         perror ("open(/dev/zero)");
308         exit (1);
309     }
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) {
313         perror ("mmap");
314         exit (1);
315     }
316     close (fd);
317     if (pthread_mutexattr_init(&mattr)) {
318         perror ("pthread_mutexattr_init");
319         exit (1);
320     }
321     if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) {
322         perror ("pthread_mutexattr_setpshared");
323         exit (1);
324     }
325     if (pthread_mutex_init(mutex, &mattr)) {
326         perror ("pthread_mutex_init");
327         exit (1);
328     }
329     sigfillset(&accept_block_mask);
330     sigdelset(&accept_block_mask, SIGHUP);
331     sigdelset(&accept_block_mask, SIGTERM);
332     sigdelset(&accept_block_mask, SIGUSR1);
333 }
334
335 void accept_mutex_on()
336 {
337     if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
338         perror("sigprocmask(SIG_BLOCK)");
339         exit (1);
340     }
341     if (pthread_mutex_lock (mutex)) {
342         perror ("pthread_mutex_lock");
343         exit (1);
344     }
345 }
346
347 void accept_mutex_off()
348 {
349     if (pthread_mutex_unlock (mutex)) {
350         perror ("pthread_mutex_unlock");
351         exit (1);
352     }
353     if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
354         perror("sigprocmask(SIG_SETMASK)");
355         exit (1);
356     }
357 }
358
359 #elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
360
361 #include <ulocks.h>
362
363 static usptr_t *us = NULL;
364 static ulock_t uslock = NULL;
365
366 #define accept_mutex_child_init()
367 #define accept_mutex_cleanup()
368
369 void accept_mutex_init(void)
370 {
371     ptrdiff_t old;
372     /* default is 8 */
373 #define CONF_INITUSERS_MAX 15
374     if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) {
375         perror("usconfig");
376         exit(-1);
377     }
378     if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
379         perror("usconfig");
380         exit(-1);
381     }
382     if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
383         perror("usconfig");
384         exit(-1);
385     }
386     if ((us = usinit("/dev/zero")) == NULL) {
387         perror("usinit");
388         exit(-1);
389     }
390     if ((uslock = usnewlock(us)) == NULL) {
391         perror("usnewlock");
392         exit(-1);
393     }
394 }
395 void accept_mutex_on()
396 {
397     switch(ussetlock(uslock)) {
398         case 1:
399             /* got lock */
400             break;
401         case 0:
402             fprintf(stderr, "didn't get lock\n");
403             exit(-1);
404         case -1:
405             perror("ussetlock");
406             exit(-1);
407     }
408 }
409 void accept_mutex_off()
410 {
411     if (usunsetlock(uslock) == -1) {
412         perror("usunsetlock");
413         exit(-1);
414     }
415 }
416 #endif
417
418
419 #ifndef USE_SHMGET_SCOREBOARD
420 static void *get_shared_mem(apr_size_t size)
421 {
422     void *result;
423
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) {
428         perror ("mmap");
429         exit (1);
430     }
431     return result;
432 }
433 #else
434 #include <sys/types.h>
435 #include <sys/ipc.h>
436 #include <sys/shm.h>
437
438 static void *get_shared_mem(apr_size_t size)
439 {
440     key_t shmkey = IPC_PRIVATE;
441     int shmid = -1;
442     void *result;
443 #ifdef MOVEBREAK
444     char *obrk;
445 #endif
446
447     if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) {
448         perror("shmget");
449         exit(1);
450     }
451
452 #ifdef MOVEBREAK
453     /*
454      * Some SysV systems place the shared segment WAY too close
455      * to the dynamic memory break point (sbrk(0)). This severely
456      * limits the use of malloc/sbrk in the program since sbrk will
457      * refuse to move past that point.
458      *
459      * To get around this, we move the break point "way up there",
460      * attach the segment and then move break back down. Ugly
461      */
462     if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
463         perror("sbrk");
464     }
465 #endif
466
467 #define BADSHMAT        ((void *)(-1))
468     if ((result = shmat(shmid, 0, 0)) == BADSHMAT) {
469         perror("shmat");
470     }
471     /*
472      * We must avoid leaving segments in the kernel's
473      * (small) tables.
474      */
475     if (shmctl(shmid, IPC_RMID, NULL) != 0) {
476         perror("shmctl(IPC_RMID)");
477     }
478     if (result == BADSHMAT)     /* now bailout */
479         exit(1);
480
481 #ifdef MOVEBREAK
482     if (obrk == (char *) -1)
483         return;                 /* nothing else to do */
484     if (sbrk(-(MOVEBREAK)) == (char *) -1) {
485         perror("sbrk 2");
486     }
487 #endif
488     return result;
489 }
490 #endif
491
492 #ifdef _POSIX_PRIORITY_SCHEDULING
493 /* don't ask */
494 #define _P __P
495 #include <sched.h>
496 #define YIELD   sched_yield()
497 #else
498 #define YIELD   do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0)
499 #endif
500
501 void main (int argc, char **argv)
502 {
503     int num_iter;
504     int num_child;
505     int i;
506     struct timeval first;
507     struct timeval last;
508     long ms;
509     int pid;
510     unsigned long *shared_counter;
511
512     if (argc != 3) {
513         fprintf (stderr, "Usage: time-sem num-child num iter\n");
514         exit (1);
515     }
516
517     num_child = atoi (argv[1]);
518     num_iter = atoi (argv[2]);
519
520     /* allocate shared memory for the shared_counter */
521     shared_counter = get_shared_mem(sizeof(*shared_counter));
522
523     /* initialize counter to 0 */
524     *shared_counter = 0;
525
526     accept_mutex_init ();
527
528     /* parent grabs mutex until done spawning children */
529     accept_mutex_on ();
530
531     for (i = 0; i < num_child; ++i) {
532         pid = fork();
533         if (pid == 0) {
534             /* child, do our thing */
535             accept_mutex_child_init();
536             for (i = 0; i < num_iter; ++i) {
537                 unsigned long tmp;
538
539                 accept_mutex_on ();
540                 tmp = *shared_counter;
541                 YIELD;
542                 *shared_counter = tmp + 1;
543                 accept_mutex_off ();
544             }
545             exit (0);
546         } else if (pid == -1) {
547             perror ("fork");
548             exit (1);
549         }
550     }
551
552     /* a quick test to see that nothing is screwed up */
553     if (*shared_counter != 0) {
554         puts ("WTF! shared_counter != 0 before the children have been started!");
555         exit (1);
556     }
557
558     gettimeofday (&first, NULL);
559     /* launch children into action */
560     accept_mutex_off ();
561     for (i = 0; i < num_child; ++i) {
562         if (wait(NULL) == -1) {
563             perror ("wait");
564         }
565     }
566     gettimeofday (&last, NULL);
567
568     if (*shared_counter != num_child * num_iter) {
569         printf ("WTF! shared_counter != num_child * num_iter!\n"
570                 "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n",
571                 *shared_counter,
572                 num_child, num_iter);
573     }
574
575     last.tv_sec -= first.tv_sec;
576     ms = last.tv_usec - first.tv_usec;
577     if (ms < 0) {
578         --last.tv_sec;
579         ms += 1000000;
580     }
581     last.tv_usec = ms;
582     printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec);
583
584     accept_mutex_cleanup();
585
586     exit(0);
587 }
588