LCOV - code coverage report
Current view: top level - aws-c-common/source - thread_shared.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 0 127 0.0 %
Date: 2021-04-23 16:28:21 Functions: 0 8 0.0 %

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

Generated by: LCOV version 1.13