LCOV - code coverage report
Current view: top level - aws-c-common/source - log_channel.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 0 192 0.0 %
Date: 2021-04-23 16:28:21 Functions: 0 9 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/log_channel.h>
       7             : 
       8             : #include <aws/common/condition_variable.h>
       9             : #include <aws/common/log_writer.h>
      10             : #include <aws/common/mutex.h>
      11             : #include <aws/common/string.h>
      12             : #include <aws/common/thread.h>
      13             : 
      14             : #include <stdio.h>
      15             : 
      16             : /*
      17             :  * Basic channel implementations - synchronized foreground, synchronized background
      18             :  */
      19             : 
      20             : struct aws_log_foreground_channel {
      21             :     struct aws_mutex sync;
      22             : };
      23             : 
      24           0 : static int s_foreground_channel_send(struct aws_log_channel *channel, struct aws_string *log_line) {
      25           0 : 
      26           0 :     struct aws_log_foreground_channel *impl = (struct aws_log_foreground_channel *)channel->impl;
      27           0 : 
      28           0 :     AWS_ASSERT(channel->writer->vtable->write);
      29           0 : 
      30           0 :     aws_mutex_lock(&impl->sync);
      31           0 :     (channel->writer->vtable->write)(channel->writer, log_line);
      32           0 :     aws_mutex_unlock(&impl->sync);
      33           0 : 
      34           0 :     /*
      35           0 :      * send is considered a transfer of ownership.  write is not a transfer of ownership.
      36           0 :      * So it's always the channel's responsibility to clean up all log lines that enter
      37           0 :      * it as soon as they are no longer needed.
      38           0 :      */
      39           0 :     aws_string_destroy(log_line);
      40           0 : 
      41           0 :     return AWS_OP_SUCCESS;
      42           0 : }
      43             : 
      44           0 : static void s_foreground_channel_clean_up(struct aws_log_channel *channel) {
      45           0 :     struct aws_log_foreground_channel *impl = (struct aws_log_foreground_channel *)channel->impl;
      46           0 : 
      47           0 :     aws_mutex_clean_up(&impl->sync);
      48           0 : 
      49           0 :     aws_mem_release(channel->allocator, impl);
      50           0 : }
      51             : 
      52             : static struct aws_log_channel_vtable s_foreground_channel_vtable = {
      53             :     .send = s_foreground_channel_send,
      54             :     .clean_up = s_foreground_channel_clean_up,
      55             : };
      56             : 
      57             : int aws_log_channel_init_foreground(
      58             :     struct aws_log_channel *channel,
      59             :     struct aws_allocator *allocator,
      60           0 :     struct aws_log_writer *writer) {
      61           0 :     struct aws_log_foreground_channel *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_log_foreground_channel));
      62           0 :     if (impl == NULL) {
      63           0 :         return AWS_OP_ERR;
      64           0 :     }
      65           0 : 
      66           0 :     if (aws_mutex_init(&impl->sync)) {
      67           0 :         aws_mem_release(allocator, impl);
      68           0 :         return AWS_OP_ERR;
      69           0 :     }
      70           0 : 
      71           0 :     channel->vtable = &s_foreground_channel_vtable;
      72           0 :     channel->allocator = allocator;
      73           0 :     channel->writer = writer;
      74           0 :     channel->impl = impl;
      75           0 : 
      76           0 :     return AWS_OP_SUCCESS;
      77           0 : }
      78             : 
      79             : struct aws_log_background_channel {
      80             :     struct aws_mutex sync;
      81             :     struct aws_thread background_thread;
      82             :     struct aws_array_list pending_log_lines;
      83             :     struct aws_condition_variable pending_line_signal;
      84             :     bool finished;
      85             : };
      86             : 
      87           0 : static int s_background_channel_send(struct aws_log_channel *channel, struct aws_string *log_line) {
      88           0 : 
      89           0 :     struct aws_log_background_channel *impl = (struct aws_log_background_channel *)channel->impl;
      90           0 : 
      91           0 :     aws_mutex_lock(&impl->sync);
      92           0 :     aws_array_list_push_back(&impl->pending_log_lines, &log_line);
      93           0 :     aws_condition_variable_notify_one(&impl->pending_line_signal);
      94           0 :     aws_mutex_unlock(&impl->sync);
      95           0 : 
      96           0 :     return AWS_OP_SUCCESS;
      97           0 : }
      98             : 
      99           0 : static void s_background_channel_clean_up(struct aws_log_channel *channel) {
     100           0 :     struct aws_log_background_channel *impl = (struct aws_log_background_channel *)channel->impl;
     101           0 : 
     102           0 :     aws_mutex_lock(&impl->sync);
     103           0 :     impl->finished = true;
     104           0 :     aws_condition_variable_notify_one(&impl->pending_line_signal);
     105           0 :     aws_mutex_unlock(&impl->sync);
     106           0 : 
     107           0 :     aws_thread_join(&impl->background_thread);
     108           0 : 
     109           0 :     aws_thread_clean_up(&impl->background_thread);
     110           0 :     aws_condition_variable_clean_up(&impl->pending_line_signal);
     111           0 :     aws_array_list_clean_up(&impl->pending_log_lines);
     112           0 :     aws_mutex_clean_up(&impl->sync);
     113           0 :     aws_mem_release(channel->allocator, impl);
     114           0 : }
     115             : 
     116             : static struct aws_log_channel_vtable s_background_channel_vtable = {
     117             :     .send = s_background_channel_send,
     118             :     .clean_up = s_background_channel_clean_up,
     119             : };
     120             : 
     121           0 : static bool s_background_wait(void *context) {
     122           0 :     struct aws_log_background_channel *impl = (struct aws_log_background_channel *)context;
     123           0 : 
     124           0 :     /*
     125           0 :      * Condition variable predicates are checked under mutex protection
     126           0 :      */
     127           0 :     return impl->finished || aws_array_list_length(&impl->pending_log_lines) > 0;
     128           0 : }
     129             : 
     130           0 : static void s_background_thread_writer(void *thread_data) {
     131           0 :     (void)thread_data;
     132           0 : 
     133           0 :     struct aws_log_channel *channel = (struct aws_log_channel *)thread_data;
     134           0 :     AWS_ASSERT(channel->writer->vtable->write);
     135           0 : 
     136           0 :     struct aws_log_background_channel *impl = (struct aws_log_background_channel *)channel->impl;
     137           0 : 
     138           0 :     struct aws_array_list log_lines;
     139           0 : 
     140           0 :     AWS_FATAL_ASSERT(aws_array_list_init_dynamic(&log_lines, channel->allocator, 10, sizeof(struct aws_string *)) == 0);
     141           0 : 
     142           0 :     while (true) {
     143           0 :         aws_mutex_lock(&impl->sync);
     144           0 :         aws_condition_variable_wait_pred(&impl->pending_line_signal, &impl->sync, s_background_wait, impl);
     145           0 : 
     146           0 :         size_t line_count = aws_array_list_length(&impl->pending_log_lines);
     147           0 :         bool finished = impl->finished;
     148           0 : 
     149           0 :         if (line_count == 0) {
     150           0 :             aws_mutex_unlock(&impl->sync);
     151           0 :             if (finished) {
     152           0 :                 break;
     153           0 :             }
     154           0 :             continue;
     155           0 :         }
     156           0 : 
     157           0 :         aws_array_list_swap_contents(&impl->pending_log_lines, &log_lines);
     158           0 :         aws_mutex_unlock(&impl->sync);
     159           0 : 
     160           0 :         /*
     161           0 :          * Consider copying these into a page-sized stack buffer (string) and then making the write calls
     162           0 :          * against it rather than the individual strings.  Might be a savings when > 1 lines (cut down on
     163           0 :          * write calls).
     164           0 :          */
     165           0 :         for (size_t i = 0; i < line_count; ++i) {
     166           0 :             struct aws_string *log_line = NULL;
     167           0 :             AWS_FATAL_ASSERT(aws_array_list_get_at(&log_lines, &log_line, i) == AWS_OP_SUCCESS);
     168           0 : 
     169           0 :             (channel->writer->vtable->write)(channel->writer, log_line);
     170           0 : 
     171           0 :             /*
     172           0 :              * send is considered a transfer of ownership.  write is not a transfer of ownership.
     173           0 :              * So it's always the channel's responsibility to clean up all log lines that enter
     174           0 :              * it as soon as they are no longer needed.
     175           0 :              */
     176           0 :             aws_string_destroy(log_line);
     177           0 :         }
     178           0 : 
     179           0 :         aws_array_list_clear(&log_lines);
     180           0 :     }
     181           0 : 
     182           0 :     aws_array_list_clean_up(&log_lines);
     183           0 : }
     184             : 
     185             : int aws_log_channel_init_background(
     186             :     struct aws_log_channel *channel,
     187             :     struct aws_allocator *allocator,
     188           0 :     struct aws_log_writer *writer) {
     189           0 :     struct aws_log_background_channel *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_log_background_channel));
     190           0 :     if (impl == NULL) {
     191           0 :         return AWS_OP_ERR;
     192           0 :     }
     193           0 : 
     194           0 :     impl->finished = false;
     195           0 : 
     196           0 :     if (aws_mutex_init(&impl->sync)) {
     197           0 :         goto clean_up_sync_init_fail;
     198           0 :     }
     199           0 : 
     200           0 :     if (aws_array_list_init_dynamic(&impl->pending_log_lines, allocator, 10, sizeof(struct aws_string *))) {
     201           0 :         goto clean_up_pending_log_lines_init_fail;
     202           0 :     }
     203           0 : 
     204           0 :     if (aws_condition_variable_init(&impl->pending_line_signal)) {
     205           0 :         goto clean_up_pending_line_signal_init_fail;
     206           0 :     }
     207           0 : 
     208           0 :     if (aws_thread_init(&impl->background_thread, allocator)) {
     209           0 :         goto clean_up_background_thread_init_fail;
     210           0 :     }
     211           0 : 
     212           0 :     channel->vtable = &s_background_channel_vtable;
     213           0 :     channel->allocator = allocator;
     214           0 :     channel->impl = impl;
     215           0 :     channel->writer = writer;
     216           0 : 
     217           0 :     /*
     218           0 :      * Logging thread should need very little stack, but let's defer this to later
     219           0 :      */
     220           0 :     struct aws_thread_options thread_options = {.stack_size = 0};
     221           0 : 
     222           0 :     if (aws_thread_launch(&impl->background_thread, s_background_thread_writer, channel, &thread_options) ==
     223           0 :         AWS_OP_SUCCESS) {
     224           0 :         return AWS_OP_SUCCESS;
     225           0 :     }
     226           0 : 
     227           0 :     aws_thread_clean_up(&impl->background_thread);
     228           0 : 
     229           0 : clean_up_background_thread_init_fail:
     230           0 :     aws_condition_variable_clean_up(&impl->pending_line_signal);
     231           0 : 
     232           0 : clean_up_pending_line_signal_init_fail:
     233           0 :     aws_array_list_clean_up(&impl->pending_log_lines);
     234           0 : 
     235           0 : clean_up_pending_log_lines_init_fail:
     236           0 :     aws_mutex_clean_up(&impl->sync);
     237           0 : 
     238           0 : clean_up_sync_init_fail:
     239           0 :     aws_mem_release(allocator, impl);
     240           0 : 
     241           0 :     return AWS_OP_ERR;
     242           0 : }
     243             : 
     244           0 : void aws_log_channel_clean_up(struct aws_log_channel *channel) {
     245           0 :     AWS_ASSERT(channel->vtable->clean_up);
     246           0 :     (channel->vtable->clean_up)(channel);
     247           0 : }

Generated by: LCOV version 1.13