thread_termination.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #ifndef __wasi__
  6. #error This example only compiles to WASM/WASI target
  7. #endif
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <assert.h>
  11. #include <pthread.h>
  12. #include <stdbool.h>
  13. #include <unistd.h>
  14. #include "wasi_thread_start.h"
  15. #define BUSY_WAIT 0
  16. #define ATOMIC_WAIT 1
  17. #define POLL_ONEOFF 2
  18. /* Change parameters here to modify the sample behavior */
  19. #define TEST_TERMINATION_BY_TRAP 0 /* Otherwise `proc_exit` termination */
  20. #define TEST_TERMINATION_IN_MAIN_THREAD 1 /* Otherwise in spawn thread */
  21. #define LONG_TASK_IMPL ATOMIC_WAIT
  22. #define TIMEOUT_SECONDS 10
  23. #define NUM_THREADS 3
  24. static pthread_barrier_t barrier;
  25. typedef struct {
  26. start_args_t base;
  27. bool throw_exception;
  28. } shared_t;
  29. void
  30. run_long_task()
  31. {
  32. #if LONG_TASK_IMPL == BUSY_WAIT
  33. for (int i = 0; i < TIMEOUT_SECONDS; i++)
  34. sleep(1);
  35. #elif LONG_TASK_IMPL == ATOMIC_WAIT
  36. __builtin_wasm_memory_atomic_wait32(0, 0, -1);
  37. #else
  38. sleep(TIMEOUT_SECONDS);
  39. #endif
  40. }
  41. void
  42. start_job()
  43. {
  44. /* Wait for all threads (including the main thread) to be ready */
  45. pthread_barrier_wait(&barrier);
  46. run_long_task(); /* Task to be interrupted */
  47. assert(false && "Thread termination test failed");
  48. }
  49. void
  50. terminate_process()
  51. {
  52. /* Wait for all other threads (including main thread) to be ready */
  53. printf("Waiting before terminating\n");
  54. pthread_barrier_wait(&barrier);
  55. printf("Force termination\n");
  56. #if TEST_TERMINATION_BY_TRAP == 1
  57. __builtin_trap();
  58. #else
  59. __wasi_proc_exit(33);
  60. #endif
  61. }
  62. void
  63. __wasi_thread_start_C(int thread_id, int *start_arg)
  64. {
  65. shared_t *data = (shared_t *)start_arg;
  66. if (data->throw_exception) {
  67. terminate_process();
  68. }
  69. else {
  70. printf("Thread running\n");
  71. start_job();
  72. }
  73. }
  74. int
  75. main(int argc, char **argv)
  76. {
  77. int thread_id = -1, i;
  78. shared_t data[NUM_THREADS] = { 0 };
  79. if (pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1) != 0) {
  80. printf("Failed to init barrier\n");
  81. return EXIT_FAILURE;
  82. }
  83. for (i = 0; i < NUM_THREADS; i++) {
  84. /* No graceful memory free to simplify the example */
  85. if (!start_args_init(&data[i].base)) {
  86. printf("Failed to allocate thread's stack\n");
  87. return EXIT_FAILURE;
  88. }
  89. }
  90. /* Create a thread that forces termination through trap or `proc_exit` */
  91. #if TEST_TERMINATION_IN_MAIN_THREAD == 1
  92. data[0].throw_exception = false;
  93. #else
  94. data[0].throw_exception = true;
  95. #endif
  96. thread_id = __wasi_thread_spawn(&data[0]);
  97. if (thread_id < 0) {
  98. printf("Failed to create thread: %d\n", thread_id);
  99. return EXIT_FAILURE;
  100. }
  101. /* Create two additional threads to test exception propagation */
  102. data[1].throw_exception = false;
  103. thread_id = __wasi_thread_spawn(&data[1]);
  104. if (thread_id < 0) {
  105. printf("Failed to create thread: %d\n", thread_id);
  106. return EXIT_FAILURE;
  107. }
  108. data[2].throw_exception = false;
  109. thread_id = __wasi_thread_spawn(&data[2]);
  110. if (thread_id < 0) {
  111. printf("Failed to create thread: %d\n", thread_id);
  112. return EXIT_FAILURE;
  113. }
  114. #if TEST_TERMINATION_IN_MAIN_THREAD == 1
  115. printf("Force termination (main thread)\n");
  116. terminate_process();
  117. #else /* TEST_TERMINATION_IN_MAIN_THREAD */
  118. printf("Main thread running\n");
  119. start_job();
  120. #endif /* TEST_TERMINATION_IN_MAIN_THREAD */
  121. return EXIT_SUCCESS;
  122. }