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