thread_shared.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/private/thread_shared.h>
  6. #include <aws/common/clock.h>
  7. #include <aws/common/condition_variable.h>
  8. #include <aws/common/linked_list.h>
  9. #include <aws/common/mutex.h>
  10. /*
  11. * lock guarding the unjoined thread count and pending join list
  12. */
  13. static struct aws_mutex s_managed_thread_lock = AWS_MUTEX_INIT;
  14. static struct aws_condition_variable s_managed_thread_signal = AWS_CONDITION_VARIABLE_INIT;
  15. static uint64_t s_default_managed_join_timeout_ns = 0;
  16. /*
  17. * The number of successfully launched managed threads (or event loop threads which participate by inc/dec) that
  18. * have not been joined yet.
  19. */
  20. static uint32_t s_unjoined_thread_count = 0;
  21. /*
  22. * A list of thread_wrapper structs for threads whose thread function has finished but join has not been called
  23. * yet for the thread.
  24. *
  25. * This list is only ever at most length one.
  26. */
  27. static struct aws_linked_list s_pending_join_managed_threads;
  28. void aws_thread_increment_unjoined_count(void) {
  29. aws_mutex_lock(&s_managed_thread_lock);
  30. ++s_unjoined_thread_count;
  31. aws_mutex_unlock(&s_managed_thread_lock);
  32. }
  33. void aws_thread_decrement_unjoined_count(void) {
  34. aws_mutex_lock(&s_managed_thread_lock);
  35. --s_unjoined_thread_count;
  36. aws_mutex_unlock(&s_managed_thread_lock);
  37. aws_condition_variable_notify_one(&s_managed_thread_signal);
  38. }
  39. size_t aws_thread_get_managed_thread_count(void) {
  40. size_t thread_count = 0;
  41. aws_mutex_lock(&s_managed_thread_lock);
  42. thread_count = s_unjoined_thread_count;
  43. aws_mutex_unlock(&s_managed_thread_lock);
  44. return thread_count;
  45. }
  46. static bool s_one_or_fewer_managed_threads_unjoined(void *context) {
  47. (void)context;
  48. return s_unjoined_thread_count <= 1;
  49. }
  50. void aws_thread_set_managed_join_timeout_ns(uint64_t timeout_in_ns) {
  51. aws_mutex_lock(&s_managed_thread_lock);
  52. s_default_managed_join_timeout_ns = timeout_in_ns;
  53. aws_mutex_unlock(&s_managed_thread_lock);
  54. }
  55. int aws_thread_join_all_managed(void) {
  56. struct aws_linked_list join_list;
  57. aws_mutex_lock(&s_managed_thread_lock);
  58. uint64_t timeout_in_ns = s_default_managed_join_timeout_ns;
  59. aws_mutex_unlock(&s_managed_thread_lock);
  60. uint64_t now_in_ns = 0;
  61. uint64_t timeout_timestamp_ns = 0;
  62. if (timeout_in_ns > 0) {
  63. aws_sys_clock_get_ticks(&now_in_ns);
  64. timeout_timestamp_ns = now_in_ns + timeout_in_ns;
  65. }
  66. bool successful = true;
  67. bool done = false;
  68. while (!done) {
  69. aws_mutex_lock(&s_managed_thread_lock);
  70. /*
  71. * We lazily join old threads as newer ones finish their thread function. This means that when called from
  72. * the main thread, there will always be one last thread (whichever completion serialized last) that is our
  73. * responsibility to join (as long as at least one managed thread was created). So we wait for a count <= 1
  74. * rather than what you'd normally expect (0).
  75. *
  76. * Absent a timeout, we only terminate if there are no threads left so it is possible to spin-wait a while
  77. * if there is a single thread still running.
  78. */
  79. if (timeout_timestamp_ns > 0) {
  80. uint64_t wait_ns = 0;
  81. /*
  82. * now_in_ns is always refreshed right before this either outside the loop before the first iteration or
  83. * after the previous wait when the overall timeout was checked.
  84. */
  85. if (now_in_ns <= timeout_timestamp_ns) {
  86. wait_ns = timeout_timestamp_ns - now_in_ns;
  87. }
  88. aws_condition_variable_wait_for_pred(
  89. &s_managed_thread_signal,
  90. &s_managed_thread_lock,
  91. wait_ns,
  92. s_one_or_fewer_managed_threads_unjoined,
  93. NULL);
  94. } else {
  95. aws_condition_variable_wait_pred(
  96. &s_managed_thread_signal, &s_managed_thread_lock, s_one_or_fewer_managed_threads_unjoined, NULL);
  97. }
  98. done = s_unjoined_thread_count == 0;
  99. aws_sys_clock_get_ticks(&now_in_ns);
  100. if (timeout_timestamp_ns != 0 && now_in_ns >= timeout_timestamp_ns) {
  101. done = true;
  102. successful = false;
  103. }
  104. aws_linked_list_init(&join_list);
  105. aws_linked_list_swap_contents(&join_list, &s_pending_join_managed_threads);
  106. aws_mutex_unlock(&s_managed_thread_lock);
  107. /*
  108. * Join against any finished threads. These threads are guaranteed to:
  109. * (1) Not be the current thread
  110. * (2) Have already ran to user thread_function completion
  111. *
  112. * The number of finished threads on any iteration is at most one.
  113. */
  114. aws_thread_join_and_free_wrapper_list(&join_list);
  115. }
  116. return successful ? AWS_OP_SUCCESS : AWS_OP_ERR;
  117. }
  118. void aws_thread_pending_join_add(struct aws_linked_list_node *node) {
  119. struct aws_linked_list join_list;
  120. aws_linked_list_init(&join_list);
  121. aws_mutex_lock(&s_managed_thread_lock);
  122. /*
  123. * Swap out the pending join threads before adding this, otherwise we'd join against ourselves which won't work
  124. */
  125. aws_linked_list_swap_contents(&join_list, &s_pending_join_managed_threads);
  126. aws_linked_list_push_back(&s_pending_join_managed_threads, node);
  127. aws_mutex_unlock(&s_managed_thread_lock);
  128. /*
  129. * Join against any finished threads. This thread (it's only ever going to be at most one)
  130. * is guaranteed to:
  131. * (1) Not be the current thread
  132. * (2) Has already ran to user thread_function completion
  133. */
  134. aws_thread_join_and_free_wrapper_list(&join_list);
  135. }
  136. void aws_thread_initialize_thread_management(void) {
  137. aws_linked_list_init(&s_pending_join_managed_threads);
  138. }