1/** \file 2 * \brief IDC system test code 3 */ 4 5/* 6 * Copyright (c) 2010, ETH Zurich. 7 * All rights reserved. 8 * 9 * This file is distributed under the terms in the attached LICENSE file. 10 * If you do not find this file, copies can be found by writing to: 11 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 12 */ 13 14#define _USE_XOPEN /* for strdup() */ 15#include <string.h> 16#include <stdio.h> 17#include <barrelfish/barrelfish.h> 18#include <barrelfish/nameservice_client.h> 19#include <barrelfish/debug.h> 20#include <if/ping_pong_defs.h> 21#include <if/ping_pong_thc.h> 22#include <thc/thc.h> 23 24#define ITERATIONS 4 25#define CANCEL_ITERS 32 26 27/* ------------------------------ COMMON ------------------------------ */ 28 29static const char *my_service_name = "idctest"; 30static const char *kind; 31 32/* ------------------------------ CLIENT ------------------------------ */ 33 34 35static void test_ooo_rpc(struct ping_pong_thc_client_binding_t *cl, 36 uint64_t val) { 37 uint64_t result_val; 38 cl->call.outoforder(cl, val, &result_val); 39 fprintf(stderr, " (%d, %d)\n", (int)val, (int)result_val); 40} 41 42static void do_nx(struct ping_pong_thc_client_binding_t *cl, 43 int i) { 44 DO_FINISH_NX({ 45 uint64_t response; 46 if (cl->call_x.outoforder(cl, i, &response) != THC_CANCELED) { 47 fprintf(stderr, " %d -> %d\n", (int)i, (int)response); 48 assert(response == i * 10); 49 } else { 50 fprintf(stderr, " URK! CANCELED RPC for %d\n", (int)i); 51 } 52 }); 53}; 54 55static void client_work(void) { 56 errval_t err; 57 uint64_t val; 58 uint64_t result_val; 59 struct ping_pong_thc_client_binding_t cl; 60 struct ping_pong_binding *b; 61 err = ping_pong_thc_connect_by_name(my_service_name, 62 get_default_waitset(), 63 IDC_BIND_FLAGS_DEFAULT, 64 &b); 65 if (err_is_fail(err)) { 66 DEBUG_ERR(err, "connect failed"); 67 abort(); 68 } 69 70 err = ping_pong_thc_init_client(&cl, b, b); 71 if (err_is_fail(err)) { 72 DEBUG_ERR(err, "init failed"); 73 abort(); 74 } 75 76 fprintf(stderr, "Starting string/array tests\n"); 77 for (int i = 0; i < 10; i ++) { 78 const char *s = strdup("foo"); 79 uint64_t len = 0, total=0; 80 fprintf(stderr, " Sending string '%s'\n", s); 81 cl.send.str0(&cl, 42, s); 82 fprintf(stderr, " Sent string\n"); 83 free((void*)s); 84 cl.recv.pong(&cl, &len); 85 fprintf(stderr, " Reply was '%d'\n", (int)len); 86 fprintf(stderr, " Sending array ['a', 'b', 'c']\n"); 87 cl.send.arr0(&cl, 42, "abc", 3); 88 fprintf(stderr, " Sent array\n"); 89 cl.recv.pong(&cl, &total); 90 fprintf(stderr, " Reply was '%d'\n\n", (int)total); 91 } 92 93 fprintf(stderr, "Starting basic tests\n"); 94 95 DO_FINISH({ 96 // Asynchronously, invoke a slower operation 97 ASYNC({ 98 uint64_t v = 0; 99 fprintf(stderr, " . . . . . . . . . . . s l o w . . . . ->\n"); 100 cl.send.slow_op(&cl, 100); 101 cl.recv.slow_reply(&cl, &v); 102 fprintf(stderr, " s l o w = %d\n", (int)v); 103 }); 104 105 // Do a series of short ping-pong messages 106 fprintf(stderr, "Doing non-RPC test:\n"); 107 val = 0; 108 do { 109 fprintf(stderr, " --- %d -->\n", (int)val); 110 cl.send.ping(&cl, val); 111 cl.recv.pong(&cl, &val); 112 } while (val < ITERATIONS); 113 114 // Do a further series of ping-pong messages via RPC, 115 // invoking the underlying send operation directly 116 fprintf(stderr, "Doing RPC test using explicit messages:\n"); 117 val = 100; 118 do { 119 cl.send.testrpc(&cl, val); 120 cl.recv.testrpc(&cl, &result_val); 121 fprintf(stderr, " (%d, %d)\n", (int)val, (int)result_val); 122 val++; 123 } while (val < 100 + ITERATIONS); 124 125 // Do a series of ping-pong messages via RPC 126 fprintf(stderr, "Doing RPC test:\n"); 127 val = 200; 128 do { 129 cl.call_seq.testrpc(&cl, val, &result_val); 130 fprintf(stderr, " (%d, %d)\n", (int)val, (int)result_val); 131 val++; 132 } while (val < 200 + ITERATIONS); 133 134 // Do a series of ping-pong messages via FIFO RPC 135 fprintf(stderr, "Doing FIFO-RPC test:\n"); 136 DO_FINISH({ 137 for (int v = 300; v < 300 + ITERATIONS; v ++) { 138 ASYNC({ 139 int my_v = v; 140 uint64_t r = 0; 141 printf("before fifo: %d\n", my_v); 142 cl.call_fifo.testrpc(&cl, my_v, &r); 143 fprintf(stderr, " (%d, %d)\n", (int)my_v, (int)r); 144 }); 145 } 146 }); 147 148 // Do a series of ping-pong messages via out-of-order RPC. 149 // The computation time is proportional to the value, so 150 // we should get the results returning in numerical order. 151 fprintf(stderr, "Doing OOO RPC test:\n"); 152 val = 400; 153 DO_FINISH({ 154 ASYNC({test_ooo_rpc(&cl, 10);}); 155 ASYNC({test_ooo_rpc(&cl, 50);}); 156 ASYNC({test_ooo_rpc(&cl, 20);}); 157 ASYNC({test_ooo_rpc(&cl, 40);}); 158 ASYNC({test_ooo_rpc(&cl, 30);}); 159 ASYNC({test_ooo_rpc(&cl, 50);}); 160 ASYNC({test_ooo_rpc(&cl, 10);}); 161 }); 162 163 fprintf(stderr, "Testing cancellation: cancelling never-received message\n"); 164 165 // Simple test, cancel a receive operation on a message we 166 // will never get 167 DO_FINISH_(cb1, { 168 ASYNC({ 169 fprintf(stderr, 170 " Starting receive...\n"); 171 int r = cl.recv_x.pong(&cl, &val); 172 fprintf(stderr, 173 " Return val %s\n", (r==THC_CANCELED) ? "CANCELLED" : "???"); 174 }); 175 fprintf(stderr, " Cancelling receive...\n"); 176 CANCEL(cb1); 177 }); 178 179 // Simple test, cancel a receive-any operation on a message we 180 // will never get 181 DO_FINISH_(cb2,{ 182 ASYNC({ 183 fprintf(stderr, 184 " Starting receive-any...\n"); 185 ping_pong_client_msg_t m; 186 int r = cl.recv_any_x(&cl, &m, 187 (struct ping_pong_client_selector) { 188 .pong=1, .testrpc=1, .testrpc2=1}); 189 fprintf(stderr, 190 " Return val %s\n", (r==THC_CANCELED) ? "CANCELLED" : "???"); 191 }); 192 fprintf(stderr, " Cancelling receive-any...\n"); 193 CANCEL(cb2); 194 }); 195 196 fprintf(stderr, "Testing cancellation: cancelling a send that blocks\n"); 197 198 DO_FINISH({ 199 // Send FIFO RPC requests as fast as possible, cancel as soon as one 200 // blocks 201 int num_sent = 0; 202 DO_FINISH_(cb3, { 203 ASYNC({ 204 while (cl.send_x.testrpc(&cl, 100+num_sent) == SYS_ERR_OK) { 205 num_sent++; 206 } 207 }); 208 fprintf(stderr, " Cancelling after %d sent\n", num_sent); 209 CANCEL(cb3); 210 }); 211 fprintf(stderr, " Cancel done, %d sent\n", num_sent); 212 for (int i = 0; i < num_sent; i ++) { 213 uint64_t response; 214 cl.recv.testrpc(&cl, &response); 215 fprintf(stderr, " Got %d\n", (int)response); 216 } 217 fprintf(stderr, " Cancel done, %d received\n", num_sent); 218 }); 219 220 // Try to receive one more... we don't expect another response, 221 // but this will detect bugs if a duplicate arrives 222 DO_FINISH_(cb4, { 223 ASYNC({ 224 uint64_t response; 225 int r = cl.recv_x.testrpc(&cl, &response); 226 assert(r == THC_CANCELED); 227 }); 228 THCYield(); 229 THCYield(); 230 THCYield(); 231 THCYield(); 232 THCYield(); 233 THCYield(); 234 fprintf(stderr, " Good: no stray responses\n"); 235 CANCEL(cb4); 236 }); 237 238 // Send a stream of testrpc and testrpc2 requests. 239 // 240 // Try to receive responses, cancelling some of the receives. 241 // Check that we get back the correct number of responses, still 242 // in sequence. 243 fprintf(stderr, "Testing cancellation: large numbers of cancellation attempts\n"); 244 DO_FINISH({ 245 ASYNC({ 246 for (int v = 1000; v < 1000 + CANCEL_ITERS; v++ ) { 247 cl.send.testrpc(&cl, v); 248 } 249 }); 250 ASYNC({ 251 for (int v = 1000; v < 1000 + CANCEL_ITERS; v++ ) { 252 cl.send.testrpc2(&cl, v); 253 } 254 }); 255 256 int v = 1000; 257 while (v < 1000 + (CANCEL_ITERS*2)) { 258 DO_FINISH_(cb5, { 259 // Start two concurrent attempts to receive RPC responses 260 ASYNC({ 261 uint64_t response; 262 if (cl.recv_x.testrpc(&cl, &response) != THC_CANCELED) { 263 fprintf(stderr, " recv %d\n", (int)response); 264 v++; 265 } 266 CANCEL(cb5); 267 }); 268 ASYNC({ 269 uint64_t response; 270 if (cl.recv_x.testrpc2(&cl, &response) != THC_CANCELED) { 271 fprintf(stderr, " recv %d\n", (int)response); 272 v++; 273 } 274 CANCEL(cb5); 275 }); 276 }); 277 } 278 }); 279 280 // Send a series of testrpc calls, cancelling some of them. Check 281 // that the calls and responses keep matching up 282 fprintf(stderr, "Testing cancellation of sequential RPC\n"); 283 for (int i = 0; i < CANCEL_ITERS; i ++) { 284 DO_FINISH_(cb6, { 285 thc_sem_t sem; 286 thc_sem_init(&sem, 0); 287 ASYNC({ 288 uint64_t response; 289 if (cl.call_seq_x.testrpc(&cl, i, &response) != THC_CANCELED) { 290 fprintf(stderr, " %d -> %d\n", (int)i, (int)response); 291 assert(response == i * 10); 292 } else { 293 fprintf(stderr, " CANCELED RPC for %d\n", (int)i); 294 } 295 thc_sem_v(&sem); 296 }); 297 if ((i % 5) == 0) { 298 thc_sem_p(&sem); 299 } 300 CANCEL(cb6); 301 }); 302 } 303 304 // Send a series of FIFO-RPC calls, cancelling some of them. Check 305 // that the calls and responses keep matching up 306 fprintf(stderr, "Testing cancellation of FIFO RPC\n"); 307 DO_FINISH({ 308 for (int i = 0; i < CANCEL_ITERS; i ++) { 309 ASYNC({ 310 int my_i = i; 311 DO_FINISH_(cb7, { 312 thc_sem_t sem; 313 thc_sem_init(&sem, 0); 314 ASYNC({ 315 uint64_t response; 316 if (cl.call_fifo_x.testrpc(&cl, my_i, &response) != THC_CANCELED) { 317 fprintf(stderr, " %d -> %d\n", (int)my_i, (int)response); 318 assert(response == my_i * 10); 319 } else { 320 fprintf(stderr, " CANCELED RPC for %d\n", (int)my_i); 321 } 322 thc_sem_v(&sem); 323 }); 324 if ((my_i % 5) == 0) { 325 thc_sem_p(&sem); 326 } 327 CANCEL(cb7); 328 }); 329 }); 330 } 331 }); 332 333 // Send a series of OOO-RPC calls, cancelling some of them. Check 334 // that the calls and responses keep matching up 335 fprintf(stderr, "Testing cancellation of OOO RPC\n"); 336 DO_FINISH({ 337 for (int i = 0; i < CANCEL_ITERS; i ++) { 338 ASYNC({ 339 int my_i = i; 340 DO_FINISH_(cb8, { 341 thc_sem_t sem; 342 thc_sem_init(&sem, 0); 343 ASYNC({ 344 uint64_t response; 345 if (cl.call_x.outoforder(&cl, my_i, &response) != THC_CANCELED) { 346 fprintf(stderr, " %d -> %d\n", (int)my_i, (int)response); 347 assert(response == my_i * 10); 348 } else { 349 fprintf(stderr, " CANCELED RPC for %d\n", (int)i); 350 } 351 thc_sem_v(&sem); 352 }); 353 if ((my_i % 5) == 0) { 354 thc_sem_p(&sem); 355 } 356 CANCEL(cb8); 357 }); 358 }); 359 } 360 }); 361 362 363 // Send a series of OOO-RPC calls, trying to cancel them all, but using 364 // non-cancellable funtcions. 365 fprintf(stderr, "Testing cancellation of non-cancellable OOO RPC\n"); 366 DO_FINISH({ 367 for (int i = 0; i < 5; i ++) { 368 ASYNC({ 369 int my_i = i; 370 DO_FINISH_(cb9, { 371 ASYNC({do_nx(&cl, my_i);}); 372 CANCEL(cb9); 373 }); 374 }); 375 } 376 }); 377 378 // Send shutdown request to server 379 fprintf(stderr, "Finished tests: sending stop request\n"); 380 cl.send.stop(&cl); 381 }); 382} 383 384/* ------------------------------ SERVER ------------------------------ */ 385 386static volatile int x = 0; 387 388static void test_service_ping(struct ping_pong_thc_service_binding_t *sv, 389 uint64_t val) { 390 val++; 391 printf(" <-- %d ---\n", (int)val); 392 sv->send.pong(sv, val); 393} 394 395static void test_service_str0(struct ping_pong_thc_service_binding_t *sv, 396 uint64_t arg, 397 char *str) { 398 int len = strlen(str); 399 printf(" (Got arg %d string %s length %d)\n", (int)arg, str, len); 400 free(str); 401 sv->send.pong(sv, len); 402} 403 404static void test_service_arr0(struct ping_pong_thc_service_binding_t *sv, 405 const char *arr, 406 size_t len) { 407 int total = 0; 408 printf(" (Got array at %p, len %d)\n", arr, (int)len); 409 for (size_t i = 0; i < len; i++) { 410 printf(" %d\n", arr[i]); 411 total += arr[i]; 412 } 413 free((void *)arr); 414 sv->send.pong(sv, total); 415} 416 417static void test_service_rpc(struct ping_pong_thc_service_binding_t *sv, 418 uint64_t val) { 419 sv->send.testrpc(sv, val*10); 420} 421 422static void test_service_rpc2(struct ping_pong_thc_service_binding_t *sv, 423 uint64_t val) { 424 sv->send.testrpc2(sv, val*10); 425} 426 427static void test_service_outoforder(struct ping_pong_thc_service_binding_t *sv, 428 uint64_t seq, 429 uint64_t val) { 430 for (int j = 0; j < val; j ++) { 431 for (int i = 0; i < 1000000; i ++) { 432 x++; 433 } 434 THCYield(); 435 } 436 sv->send.outoforder(sv, seq, val*10); 437} 438 439static void test_service_slow_reply(struct ping_pong_thc_service_binding_t *sv, 440 uint64_t val) { 441 val++; 442 for (int j = 0; j < 5; j ++) { 443 for (int i = 0; i < 1000000; i ++) { 444 x++; 445 } 446 THCYield(); 447 } 448 printf(" < - - %d - - - . . . . . . . . . . . \n", (int)val); 449 sv->send.slow_reply(sv, val); 450} 451 452static void service_client(struct ping_pong_thc_service_binding_t *sv) { 453 DO_FINISH({ 454 bool stop = false; 455 while (!stop) { 456 ping_pong_service_msg_t m; 457 sv->recv_any(sv, &m, (struct ping_pong_service_selector) { 458 .ping=1, .stop=1, .testrpc=1, .testrpc2=1, .outoforder=1, .slow_op=1, .str0=1, .arr0=1}); 459 switch (m.msg) { 460 case ping_pong_slow_op: 461 ASYNC({test_service_slow_reply(sv, m.args.slow_op.val);}); 462 break; 463 464 case ping_pong_ping: 465 ASYNC({test_service_ping(sv, m.args.ping.val);}); 466 break; 467 468 case ping_pong_testrpc: 469 test_service_rpc(sv, m.args.testrpc.in.testin); // Sync: ensure FIFO 470 break; 471 472 case ping_pong_testrpc2: 473 test_service_rpc2(sv, m.args.testrpc.in.testin); // Sync: ensure FIFO 474 break; 475 476 case ping_pong_outoforder: 477 ASYNC({test_service_outoforder(sv, 478 m.args.outoforder.in.seq_in, 479 m.args.outoforder.in.testin);}); 480 break; 481 482 case ping_pong_str0: 483 test_service_str0(sv, 484 m.args.str0.arg1, 485 m.args.str0.s); 486 break; 487 488 case ping_pong_arr0: 489 test_service_arr0(sv, 490 m.args.arr0.a, 491 m.args.arr0.l); 492 break; 493 494 495 case ping_pong_stop: 496 fprintf(stderr, "Service: stopping\n"); 497 stop = 1; 498 break; 499 500 default: 501 assert(0 && "Unexpected message"); 502 break; 503 } 504 } 505 }); 506} 507 508static void server_work(void) { 509 struct ping_pong_thc_service_binding_t *sv; 510 struct ping_pong_binding *b; 511 struct ping_pong_thc_export_info info; 512 errval_t err; 513 iref_t iref; 514 515 printf("Starting server_work\n"); 516 err = ping_pong_thc_export(&info, 517 my_service_name, 518 get_default_waitset(), 519 IDC_EXPORT_FLAGS_DEFAULT, 520 &iref); 521 printf("Done export iref=%"PRIuIREF"\n", iref); 522 if (err_is_fail(err)) { 523 DEBUG_ERR(err, "export failed"); 524 abort(); 525 } 526 527 DO_FINISH({ 528 while (1) { 529 printf("server waiting for connection\n"); 530 err = ping_pong_thc_accept(&info, &b); 531 if (err_is_fail(err)) { 532 DEBUG_ERR(err, "accept failed"); 533 abort(); 534 } 535 536 sv = malloc(sizeof(struct ping_pong_thc_service_binding_t)); 537 if (sv == NULL) { 538 DEBUG_ERR(err, "malloc failed"); 539 abort(); 540 } 541 542 err = ping_pong_thc_init_service(sv, b, b); 543 if (err_is_fail(err)) { 544 DEBUG_ERR(err, "init failed"); 545 abort(); 546 } 547 548 printf("Got service %p\n", sv); 549 ASYNC({service_client(sv);}); 550 } 551 }); 552} 553 554/* ------------------------------ MAIN ------------------------------ */ 555 556int main(int argc, char *argv[]) 557{ 558 // Allow arbitrary early parameters (e.g., "boot" when invoked 559 // directly from menu.lst by monitor) 560 if (argc >= 2 && strcmp(argv[argc-1], "client") == 0) { 561 kind = "client"; 562 client_work(); 563 } else if (argc >= 2 && strcmp(argv[argc-1], "server") == 0) { 564 kind = "server"; 565 server_work(); 566 } else { 567 printf("Usage: %s ... client|server\n", argv[0]); 568 return EXIT_FAILURE; 569 } 570 571 printf("%s %s DONE!\n", argv[0], kind); 572 return EXIT_SUCCESS; 573} 574 575