]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/MPMMTest/MPMMtest.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / tools / tests / MPMMTest / MPMMtest.c
1 #include <AvailabilityMacros.h>
2 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
3 #include </System/Library/Frameworks/System.framework/PrivateHeaders/mach/thread_policy.h>
4 #endif
5
6 #include <pthread.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <err.h>
11
12 #include <pthread.h>
13 #include <mach/mach.h>
14 #include <mach/mach_error.h>
15 #include <mach/notify.h>
16 #include <servers/bootstrap.h>
17 #include <sys/types.h>
18 #include <sys/time.h>
19 #include <sys/signal.h>
20
21 #define MAX(A, B) ((A) < (B) ? (B) : (A))
22
23
24 typedef struct {
25 mach_msg_header_t header;
26 mach_msg_trailer_t trailer; // subtract this when sending
27 } ipc_trivial_message;
28
29 typedef struct {
30 mach_msg_header_t header;
31 u_int32_t numbers[0];
32 mach_msg_trailer_t trailer; // subtract this when sending
33 } ipc_inline_message;
34
35 typedef struct {
36 mach_msg_header_t header;
37 mach_msg_body_t body;
38 mach_msg_ool_descriptor_t descriptor;
39 mach_msg_trailer_t trailer; // subtract this when sending
40 } ipc_complex_message;
41
42 enum {
43 msg_type_trivial = 0,
44 msg_type_inline = 1,
45 msg_type_complex = 2
46 };
47
48 struct port_args {
49 int server_num;
50 int req_size;
51 mach_msg_header_t *req_msg;
52 int reply_size;
53 mach_msg_header_t *reply_msg;
54 mach_port_t port;
55 mach_port_t set;
56 };
57
58 typedef union {
59 pid_t pid;
60 pthread_t tid;
61 } thread_id_t;
62
63 /* Global options */
64 static boolean_t verbose = FALSE;
65 static boolean_t affinity = FALSE;
66 static boolean_t timeshare = FALSE;
67 static boolean_t threaded = FALSE;
68 static boolean_t oneway = FALSE;
69 static boolean_t useset = FALSE;
70 int msg_type;
71 int num_ints;
72 int num_msgs;
73 int num_clients;
74 int num_servers;
75 int client_delay;
76 int client_spin;
77 int client_pages;
78 int portcount = 1;
79 char **server_port_name;
80
81 void signal_handler(int sig) {
82 }
83
84 void usage(const char *progname) {
85 fprintf(stderr, "usage: %s [options]\n", progname);
86 fprintf(stderr, "where options are:\n");
87 fprintf(stderr, " -affinity\t\tthreads use affinity\n");
88 fprintf(stderr, " -timeshare\t\tthreads use timeshare\n");
89 fprintf(stderr, " -threaded\t\tuse (p)threads\n");
90 fprintf(stderr, " -verbose\t\tbe verbose\n");
91 fprintf(stderr, " -oneway\t\tdo not request return reply\n");
92 fprintf(stderr, " -count num\t\tnumber of messages to send\n");
93 fprintf(stderr, " -type trivial|inline|complex\ttype of messages to send\n");
94 fprintf(stderr, " -numints num\tnumber of 32-bit ints to send in messages\n");
95 fprintf(stderr, " -servers num\tnumber of servers threads to run\n");
96 fprintf(stderr, " -clients num\tnumber of clients per server\n");
97 fprintf(stderr, " -delay num\t\tmicroseconds to sleep clients between messages\n");
98 fprintf(stderr, " -work num\t\tmicroseconds of client work\n");
99 fprintf(stderr, " -pages num\t\tpages of memory touched by client work\n");
100 fprintf(stderr, " -set num\t\tuse a portset stuffed with num ports in server\n");
101 fprintf(stderr, "default values are:\n");
102 fprintf(stderr, " . no affinity\n");
103 fprintf(stderr, " . not timeshare\n");
104 fprintf(stderr, " . not verbose\n");
105 fprintf(stderr, " . not oneway\n");
106 fprintf(stderr, " . client sends 100000 messages\n");
107 fprintf(stderr, " . inline message type\n");
108 fprintf(stderr, " . 64 32-bit integers in inline/complex messages\n");
109 fprintf(stderr, " . (num_available_processors+1)%%2 servers\n");
110 fprintf(stderr, " . 4 clients per server\n");
111 fprintf(stderr, " . no delay\n");
112 exit(1);
113 }
114
115 void parse_args(int argc, char *argv[]) {
116 host_basic_info_data_t info;
117 mach_msg_type_number_t count;
118 kern_return_t result;
119
120 /* Initialize defaults */
121 msg_type = msg_type_trivial;
122 num_ints = 64;
123 num_msgs = 100000;
124 client_delay = 0;
125 num_clients = 4;
126
127 count = HOST_BASIC_INFO_COUNT;
128 result = host_info(mach_host_self(), HOST_BASIC_INFO,
129 (host_info_t)&info, &count);
130 if (result == KERN_SUCCESS && info.avail_cpus > 1)
131 num_servers = info.avail_cpus / 2;
132 else
133 num_servers = 1;
134
135 const char *progname = argv[0];
136 argc--; argv++;
137 while (0 < argc) {
138 if (0 == strcmp("-verbose", argv[0])) {
139 verbose = TRUE;
140 argc--; argv++;
141 } else if (0 == strcmp("-affinity", argv[0])) {
142 affinity = TRUE;
143 argc--; argv++;
144 } else if (0 == strcmp("-timeshare", argv[0])) {
145 timeshare = TRUE;
146 argc--; argv++;
147 } else if (0 == strcmp("-threaded", argv[0])) {
148 threaded = TRUE;
149 argc--; argv++;
150 } else if (0 == strcmp("-oneway", argv[0])) {
151 oneway = TRUE;
152 argc--; argv++;
153 } else if (0 == strcmp("-type", argv[0])) {
154 if (argc < 2)
155 usage(progname);
156 if (0 == strcmp("trivial", argv[1])) {
157 msg_type = msg_type_trivial;
158 } else if (0 == strcmp("inline", argv[1])) {
159 msg_type = msg_type_inline;
160 } else if (0 == strcmp("complex", argv[1])) {
161 msg_type = msg_type_complex;
162 } else
163 usage(progname);
164 argc -= 2; argv += 2;
165 } else if (0 == strcmp("-numints", argv[0])) {
166 if (argc < 2)
167 usage(progname);
168 num_ints = strtoul(argv[1], NULL, 0);
169 argc -= 2; argv += 2;
170 } else if (0 == strcmp("-count", argv[0])) {
171 if (argc < 2)
172 usage(progname);
173 num_msgs = strtoul(argv[1], NULL, 0);
174 argc -= 2; argv += 2;
175 } else if (0 == strcmp("-clients", argv[0])) {
176 if (argc < 2)
177 usage(progname);
178 num_clients = strtoul(argv[1], NULL, 0);
179 argc -= 2; argv += 2;
180 } else if (0 == strcmp("-servers", argv[0])) {
181 if (argc < 2)
182 usage(progname);
183 num_servers = strtoul(argv[1], NULL, 0);
184 argc -= 2; argv += 2;
185 } else if (0 == strcmp("-delay", argv[0])) {
186 if (argc < 2)
187 usage(progname);
188 client_delay = strtoul(argv[1], NULL, 0);
189 argc -= 2; argv += 2;
190 } else if (0 == strcmp("-spin", argv[0])) {
191 if (argc < 2)
192 usage(progname);
193 client_spin = strtoul(argv[1], NULL, 0);
194 argc -= 2; argv += 2;
195 } else if (0 == strcmp("-pages", argv[0])) {
196 if (argc < 2)
197 usage(progname);
198 client_pages = strtoul(argv[1], NULL, 0);
199 argc -= 2; argv += 2;
200 } else if (0 == strcmp("-set", argv[0])) {
201 if (argc < 2)
202 usage(progname);
203 portcount = strtoul(argv[1], NULL, 0);
204 useset = TRUE;
205 argc -= 2; argv += 2;
206 argc--; argv++;
207 } else
208 usage(progname);
209 }
210 }
211
212 void setup_server_ports(struct port_args *ports)
213 {
214 kern_return_t ret = 0;
215 mach_port_t bsport;
216 mach_port_t port;
217 int i;
218
219 ports->req_size = MAX(sizeof(ipc_inline_message) +
220 sizeof(u_int32_t) * num_ints,
221 sizeof(ipc_complex_message));
222 ports->reply_size = sizeof(ipc_trivial_message) -
223 sizeof(mach_msg_trailer_t);
224 ports->req_msg = malloc(ports->req_size);
225 ports->reply_msg = malloc(ports->reply_size);
226
227 if (useset) {
228 ret = mach_port_allocate(mach_task_self(),
229 MACH_PORT_RIGHT_PORT_SET,
230 &(ports->set));
231 if (KERN_SUCCESS != ret) {
232 mach_error("mach_port_allocate(SET): ", ret);
233 exit(1);
234 }
235 }
236
237 /* stuff the portset with ports */
238 for (i=0; i < portcount; i++) {
239 ret = mach_port_allocate(mach_task_self(),
240 MACH_PORT_RIGHT_RECEIVE,
241 &port);
242 if (KERN_SUCCESS != ret) {
243 mach_error("mach_port_allocate(PORT): ", ret);
244 exit(1);
245 }
246
247 if (useset) {
248 ret = mach_port_move_member(mach_task_self(),
249 port,
250 ports->set);
251 if (KERN_SUCCESS != ret) {
252 mach_error("mach_port_move_member(): ", ret);
253 exit(1);
254 }
255 }
256 }
257
258 /* use the last one as the real port */
259 ports->port = port;
260
261 ret = mach_port_insert_right(mach_task_self(),
262 ports->port,
263 ports->port,
264 MACH_MSG_TYPE_MAKE_SEND);
265 if (KERN_SUCCESS != ret) {
266 mach_error("mach_port_insert_right(): ", ret);
267 exit(1);
268 }
269
270 ret = task_get_bootstrap_port(mach_task_self(), &bsport);
271 if (KERN_SUCCESS != ret) {
272 mach_error("task_get_bootstrap_port(): ", ret);
273 exit(1);
274 }
275
276 if (verbose) {
277 printf("server waiting for IPC messages from client on port '%s'.\n",
278 server_port_name[ports->server_num]);
279 }
280 ret = bootstrap_register(bsport,
281 server_port_name[ports->server_num],
282 ports->port);
283 if (KERN_SUCCESS != ret) {
284 mach_error("bootstrap_register(): ", ret);
285 exit(1);
286 }
287 }
288
289 void setup_client_ports(struct port_args *ports)
290 {
291 kern_return_t ret = 0;
292 switch(msg_type) {
293 case msg_type_trivial:
294 ports->req_size = sizeof(ipc_trivial_message);
295 break;
296 case msg_type_inline:
297 ports->req_size = sizeof(ipc_inline_message) +
298 sizeof(u_int32_t) * num_ints;
299 break;
300 case msg_type_complex:
301 ports->req_size = sizeof(ipc_complex_message);
302 break;
303 }
304 ports->req_size -= sizeof(mach_msg_trailer_t);
305 ports->reply_size = sizeof(ipc_trivial_message);
306 ports->req_msg = malloc(ports->req_size);
307 ports->reply_msg = malloc(ports->reply_size);
308
309 ret = mach_port_allocate(mach_task_self(),
310 MACH_PORT_RIGHT_RECEIVE,
311 &(ports->port));
312 if (KERN_SUCCESS != ret) {
313 mach_error("mach_port_allocate(): ", ret);
314 exit(1);
315 }
316 if (verbose) {
317 printf("Client sending %d %s IPC messages to port '%s' in %s mode.\n",
318 num_msgs, (msg_type == msg_type_inline) ?
319 "inline" : ((msg_type == msg_type_complex) ?
320 "complex" : "trivial"),
321 server_port_name[ports->server_num],
322 (oneway ? "oneway" : "rpc"));
323 }
324
325 }
326
327
328 static void
329 thread_setup(int tag) {
330 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
331 kern_return_t ret;
332 thread_extended_policy_data_t epolicy;
333 thread_affinity_policy_data_t policy;
334
335 if (!timeshare) {
336 epolicy.timeshare = FALSE;
337 ret = thread_policy_set(
338 mach_thread_self(), THREAD_EXTENDED_POLICY,
339 (thread_policy_t) &epolicy,
340 THREAD_EXTENDED_POLICY_COUNT);
341 if (ret != KERN_SUCCESS)
342 printf("thread_policy_set(THREAD_EXTENDED_POLICY) returned %d\n", ret);
343 }
344
345 if (affinity) {
346 policy.affinity_tag = tag;
347 ret = thread_policy_set(
348 mach_thread_self(), THREAD_AFFINITY_POLICY,
349 (thread_policy_t) &policy,
350 THREAD_AFFINITY_POLICY_COUNT);
351 if (ret != KERN_SUCCESS)
352 printf("thread_policy_set(THREAD_AFFINITY_POLICY) returned %d\n", ret);
353 }
354 #endif
355 }
356
357 void *
358 server(void *serverarg)
359 {
360 struct port_args args;
361 int idx;
362 kern_return_t ret;
363 int totalmsg = num_msgs * num_clients;
364 mach_port_t recv_port;
365
366 args.server_num = (int) (long) serverarg;
367 setup_server_ports(&args);
368
369 thread_setup(args.server_num + 1);
370
371 recv_port = (useset) ? args.set : args.port;
372
373 for (idx = 0; idx < totalmsg; idx++) {
374 if (verbose)
375 printf("server awaiting message %d\n", idx);
376 ret = mach_msg(args.req_msg,
377 MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE,
378 0,
379 args.req_size,
380 recv_port,
381 MACH_MSG_TIMEOUT_NONE,
382 MACH_PORT_NULL);
383 if (MACH_RCV_INTERRUPTED == ret)
384 break;
385 if (MACH_MSG_SUCCESS != ret) {
386 if (verbose)
387 printf("mach_msg() ret=%d", ret);
388 mach_error("mach_msg (receive): ", ret);
389 exit(1);
390 }
391 if (verbose)
392 printf("server received message %d\n", idx);
393 if (args.req_msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
394 ret = vm_deallocate(mach_task_self(),
395 (vm_address_t)((ipc_complex_message *)args.req_msg)->descriptor.address,
396 ((ipc_complex_message *)args.req_msg)->descriptor.size);
397 }
398
399 if (1 == args.req_msg->msgh_id) {
400 if (verbose)
401 printf("server sending reply %d\n", idx);
402 args.reply_msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0);
403 args.reply_msg->msgh_size = args.reply_size;
404 args.reply_msg->msgh_remote_port = args.req_msg->msgh_remote_port;
405 args.reply_msg->msgh_local_port = MACH_PORT_NULL;
406 args.reply_msg->msgh_id = 2;
407 ret = mach_msg(args.reply_msg,
408 MACH_SEND_MSG,
409 args.reply_size,
410 0,
411 MACH_PORT_NULL,
412 MACH_MSG_TIMEOUT_NONE,
413 MACH_PORT_NULL);
414 if (MACH_MSG_SUCCESS != ret) {
415 mach_error("mach_msg (send): ", ret);
416 exit(1);
417 }
418 }
419 }
420 }
421
422 static inline void
423 client_spin_loop(unsigned count, void (fn)(void))
424 {
425 while (count--)
426 fn();
427 }
428
429 static long dummy_memory;
430 static long *client_memory = &dummy_memory;
431 static void
432 client_work_atom(void)
433 {
434 static int i;
435
436 if (++i > client_pages * PAGE_SIZE / sizeof(long))
437 i = 0;
438 client_memory[i] = 0;
439 }
440
441 static int calibration_count = 10000;
442 static int calibration_usec;
443 static void *
444 calibrate_client_work(void)
445 {
446 long dummy;
447 struct timeval nowtv;
448 struct timeval warmuptv = { 0, 100 * 1000 }; /* 100ms */
449 struct timeval starttv;
450 struct timeval endtv;
451
452 if (client_spin) {
453 /* Warm-up the stepper first... */
454 gettimeofday(&nowtv, NULL);
455 timeradd(&nowtv, &warmuptv, &endtv);
456 do {
457 client_spin_loop(calibration_count, client_work_atom);
458 gettimeofday(&nowtv, NULL);
459 } while (timercmp(&nowtv, &endtv, < ));
460
461 /* Now do the calibration */
462 while (TRUE) {
463 gettimeofday(&starttv, NULL);
464 client_spin_loop(calibration_count, client_work_atom);
465 gettimeofday(&endtv, NULL);
466 if (endtv.tv_sec - starttv.tv_sec > 1) {
467 calibration_count /= 10;
468 continue;
469 }
470 calibration_usec = endtv.tv_usec - starttv.tv_usec;
471 if (endtv.tv_usec < starttv.tv_usec) {
472 calibration_usec += 1000000;
473 }
474 if (calibration_usec < 1000) {
475 calibration_count *= 10;
476 continue;
477 }
478 calibration_count /= calibration_usec;
479 break;
480 }
481 if (verbose)
482 printf("calibration_count=%d calibration_usec=%d\n",
483 calibration_count, calibration_usec);
484 }
485 }
486
487 static void *
488 client_work(void)
489 {
490
491 if (client_spin) {
492 client_spin_loop(calibration_count*client_spin,
493 client_work_atom);
494 }
495
496 if (client_delay) {
497 usleep(client_delay);
498 }
499 }
500
501 void *client(void *threadarg)
502 {
503 struct port_args args;
504 int idx;
505 mach_msg_header_t *req, *reply;
506 mach_port_t bsport, servport;
507 kern_return_t ret;
508 long server_num = (long) threadarg;
509 void *ints = malloc(sizeof(u_int32_t) * num_ints);
510
511 if (verbose)
512 printf("client(%d) started, server port name %s\n",
513 server_num, server_port_name[server_num]);
514
515 args.server_num = server_num;
516 thread_setup(server_num + 1);
517
518 /* find server port */
519 ret = task_get_bootstrap_port(mach_task_self(), &bsport);
520 if (KERN_SUCCESS != ret) {
521 mach_error("task_get_bootstrap_port(): ", ret);
522 exit(1);
523 }
524 ret = bootstrap_look_up(bsport,
525 server_port_name[server_num],
526 &servport);
527 if (KERN_SUCCESS != ret) {
528 mach_error("bootstrap_look_up(): ", ret);
529 exit(1);
530 }
531
532 setup_client_ports(&args);
533
534 /* Allocate and touch memory */
535 if (client_pages) {
536 unsigned i;
537 client_memory = (long *) malloc(client_pages * PAGE_SIZE);
538 for (i = 0; i < client_pages; i++)
539 client_memory[i * PAGE_SIZE / sizeof(long)] = 0;
540 }
541
542 /* start message loop */
543 for (idx = 0; idx < num_msgs; idx++) {
544 req = args.req_msg;
545 reply = args.reply_msg;
546
547 req->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
548 MACH_MSG_TYPE_MAKE_SEND_ONCE);
549 req->msgh_size = args.req_size;
550 req->msgh_remote_port = servport;
551 req->msgh_local_port = args.port;
552 req->msgh_id = oneway ? 0 : 1;
553 if (msg_type == msg_type_complex) {
554 (req)->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
555 ((ipc_complex_message *)req)->body.msgh_descriptor_count = 1;
556 ((ipc_complex_message *)req)->descriptor.address = ints;
557 ((ipc_complex_message *)req)->descriptor.size =
558 num_ints * sizeof(u_int32_t);
559 ((ipc_complex_message *)req)->descriptor.deallocate = FALSE;
560 ((ipc_complex_message *)req)->descriptor.copy = MACH_MSG_VIRTUAL_COPY;
561 ((ipc_complex_message *)req)->descriptor.type = MACH_MSG_OOL_DESCRIPTOR;
562 }
563 if (verbose)
564 printf("client sending message %d\n", idx);
565 ret = mach_msg(req,
566 MACH_SEND_MSG,
567 args.req_size,
568 0,
569 MACH_PORT_NULL,
570 MACH_MSG_TIMEOUT_NONE,
571 MACH_PORT_NULL);
572 if (MACH_MSG_SUCCESS != ret) {
573 mach_error("mach_msg (send): ", ret);
574 fprintf(stderr, "bailing after %u iterations\n", idx);
575 exit(1);
576 break;
577 }
578 if (!oneway) {
579 if (verbose)
580 printf("client awaiting reply %d\n", idx);
581 reply->msgh_bits = 0;
582 reply->msgh_size = args.reply_size;
583 reply->msgh_local_port = args.port;
584 ret = mach_msg(args.reply_msg,
585 MACH_RCV_MSG|MACH_RCV_INTERRUPT,
586 0,
587 args.reply_size,
588 args.port,
589 MACH_MSG_TIMEOUT_NONE,
590 MACH_PORT_NULL);
591 if (MACH_MSG_SUCCESS != ret) {
592 mach_error("mach_msg (receive): ", ret);
593 fprintf(stderr, "bailing after %u iterations\n",
594 idx);
595 exit(1);
596 }
597 if (verbose)
598 printf("client received reply %d\n", idx);
599 }
600
601 client_work();
602 }
603
604 free(ints);
605 return;
606 }
607
608 static void
609 thread_spawn(thread_id_t *thread, void *(fn)(void *), void *arg) {
610 if (threaded) {
611 kern_return_t ret;
612 ret = pthread_create(
613 &thread->tid,
614 NULL,
615 fn,
616 arg);
617 if (ret != 0)
618 err(1, "pthread_create()");
619 if (verbose)
620 printf("created pthread 0x%x\n", thread->tid);
621 } else {
622 thread->pid = fork();
623 if (thread->pid == 0) {
624 if (verbose)
625 printf("calling 0x%x(0x%x)\n", fn, arg);
626 fn(arg);
627 exit(0);
628 }
629 if (verbose)
630 printf("forked pid %d\n", thread->pid);
631 }
632 }
633
634 static void
635 thread_join(thread_id_t *thread) {
636 if (threaded) {
637 kern_return_t ret;
638 if (verbose)
639 printf("joining thread 0x%x\n", thread->tid);
640 ret = pthread_join(thread->tid, NULL);
641 if (ret != KERN_SUCCESS)
642 err(1, "pthread_join(0x%x)", thread->tid);
643 } else {
644 int stat;
645 if (verbose)
646 printf("waiting for pid %d\n", thread->pid);
647 waitpid(thread->pid, &stat, 0);
648 }
649 }
650
651 static void
652 wait_for_servers(void)
653 {
654 int i;
655 int retry_count = 10;
656 mach_port_t bsport, servport;
657 kern_return_t ret;
658
659 /* find server port */
660 ret = task_get_bootstrap_port(mach_task_self(), &bsport);
661 if (KERN_SUCCESS != ret) {
662 mach_error("task_get_bootstrap_port(): ", ret);
663 exit(1);
664 }
665
666 while (retry_count-- > 0) {
667 for (i = 0; i < num_servers; i++) {
668 ret = bootstrap_look_up(bsport,
669 server_port_name[i],
670 &servport);
671 if (ret != KERN_SUCCESS) {
672 break;
673 }
674 }
675 if (ret == KERN_SUCCESS)
676 return;
677 usleep(100 * 1000); /* 100ms */
678 }
679 fprintf(stderr, "Server(s) failed to register\n");
680 exit(1);
681 }
682
683 int main(int argc, char *argv[])
684 {
685 int i;
686 int j;
687 thread_id_t *client_id;
688 thread_id_t *server_id;
689
690 signal(SIGINT, signal_handler);
691 parse_args(argc, argv);
692
693 calibrate_client_work();
694
695 /*
696 * If we're using affinity create an empty namespace now
697 * so this is shared by all our offspring.
698 */
699 if (affinity)
700 thread_setup(0);
701
702 server_id = (thread_id_t *) malloc(num_servers * sizeof(thread_id_t));
703 server_port_name = (char **) malloc(num_servers * sizeof(char *));
704 if (verbose)
705 printf("creating %d servers\n", num_servers);
706 for (i = 0; i < num_servers; i++) {
707 server_port_name[i] = (char *) malloc(sizeof("PORT.pppppp.xx"));
708 /* PORT names include pid of main process for disambiguation */
709 sprintf(server_port_name[i], "PORT.%06d.%02d", getpid(), i);
710 thread_spawn(&server_id[i], server, (void *) (long) i);
711 }
712
713 int totalclients = num_servers * num_clients;
714 int totalmsg = num_msgs * totalclients;
715 struct timeval starttv, endtv, deltatv;
716
717 /*
718 * Wait for all servers to have registered all ports before starting
719 * the clients and the clock.
720 */
721 wait_for_servers();
722
723 printf("%d server%s, %d client%s per server (%d total) %u messages...",
724 num_servers, (num_servers > 1)? "s" : "",
725 num_clients, (num_clients > 1)? "s" : "",
726 totalclients,
727 totalmsg);
728 fflush(stdout);
729
730 /* Call gettimeofday() once and throw away result; some implementations
731 * (like Mach's) cache some time zone info on first call.
732 */
733 gettimeofday(&starttv, NULL);
734 gettimeofday(&starttv, NULL);
735
736 client_id = (thread_id_t *) malloc(totalclients * sizeof(thread_id_t));
737 if (verbose)
738 printf("creating %d clients\n", totalclients);
739 for (i = 0; i < num_servers; i++) {
740 for (j = 0; j < num_clients; j++) {
741 thread_spawn(
742 &client_id[(i*num_clients) + j],
743 client,
744 (void *) (long) i);
745 }
746 }
747
748 /* Wait for servers to complete */
749 for (i = 0; i < num_servers; i++) {
750 thread_join(&server_id[i]);
751 }
752
753 gettimeofday(&endtv, NULL);
754
755 for (i = 0; i < totalclients; i++) {
756 thread_join(&client_id[i]);
757 }
758
759 /* report results */
760 deltatv.tv_sec = endtv.tv_sec - starttv.tv_sec;
761 deltatv.tv_usec = endtv.tv_usec - starttv.tv_usec;
762 if (endtv.tv_usec < starttv.tv_usec) {
763 deltatv.tv_sec--;
764 deltatv.tv_usec += 1000000;
765 }
766
767 double dsecs = (double) deltatv.tv_sec +
768 1.0E-6 * (double) deltatv.tv_usec;
769
770 printf(" in %u.%03u seconds\n",
771 deltatv.tv_sec, deltatv.tv_usec/1000);
772 printf(" throughput in messages/sec: %g\n",
773 (double)totalmsg / dsecs);
774 printf(" average message latency (usec): %2.3g\n",
775 dsecs * 1.0E6 / (double) totalmsg);
776
777 return (0);
778
779 }