LCOV - code coverage report
Current view: top level - core/or - scheduler_vanilla.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 42 54 77.8 %
Date: 2021-11-24 03:28:48 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * @file scheduler_vanilla.c
       6             :  * @brief "Vanilla" (pre-KIST) cell scheduler code.
       7             :  **/
       8             : 
       9             : #include "core/or/or.h"
      10             : #include "app/config/config.h"
      11             : #define CHANNEL_OBJECT_PRIVATE
      12             : #include "core/or/channel.h"
      13             : #define SCHEDULER_PRIVATE
      14             : #include "core/or/scheduler.h"
      15             : 
      16             : /*****************************************************************************
      17             :  * Other internal data
      18             :  *****************************************************************************/
      19             : 
      20             : /* Maximum cells to flush in a single call to channel_flush_some_cells(); */
      21             : #define MAX_FLUSH_CELLS 1000
      22             : 
      23             : /*****************************************************************************
      24             :  * Externally called function implementations
      25             :  *****************************************************************************/
      26             : 
      27             : /* Return true iff the scheduler has work to perform. */
      28             : static int
      29           7 : have_work(void)
      30             : {
      31           7 :   smartlist_t *cp = get_channels_pending();
      32           7 :   IF_BUG_ONCE(!cp) {
      33             :     return 0; // channels_pending doesn't exist so... no work?
      34             :   }
      35           7 :   return smartlist_len(cp) > 0;
      36             : }
      37             : 
      38             : /** Re-trigger the scheduler in a way safe to use from the callback */
      39             : 
      40             : static void
      41           7 : vanilla_scheduler_schedule(void)
      42             : {
      43           7 :   if (!have_work()) {
      44             :     return;
      45             :   }
      46             : 
      47             :   /* Activate our event so it can process channels. */
      48           7 :   scheduler_ev_active();
      49             : }
      50             : 
      51             : static void
      52           2 : vanilla_scheduler_run(void)
      53             : {
      54           2 :   int n_cells, n_chans_before, n_chans_after;
      55           2 :   ssize_t flushed, flushed_this_time;
      56           2 :   smartlist_t *cp = get_channels_pending();
      57           2 :   smartlist_t *to_readd = NULL;
      58           2 :   channel_t *chan = NULL;
      59             : 
      60           2 :   log_debug(LD_SCHED, "We have a chance to run the scheduler");
      61             : 
      62           2 :   n_chans_before = smartlist_len(cp);
      63             : 
      64           6 :   while (smartlist_len(cp) > 0) {
      65             :     /* Pop off a channel */
      66           4 :     chan = smartlist_pqueue_pop(cp,
      67             :                                 scheduler_compare_channels,
      68             :                                 offsetof(channel_t, sched_heap_idx));
      69           4 :     IF_BUG_ONCE(!chan) {
      70             :       /* Some-freaking-how a NULL got into the channels_pending. That should
      71             :        * never happen, but it should be harmless to ignore it and keep looping.
      72             :        */
      73           0 :       continue;
      74             :     }
      75             : 
      76             :     /* Figure out how many cells we can write */
      77           4 :     n_cells = channel_num_cells_writeable(chan);
      78           4 :     if (n_cells > 0) {
      79           3 :       log_debug(LD_SCHED,
      80             :                 "Scheduler saw pending channel %"PRIu64 " at %p with "
      81             :                 "%d cells writeable",
      82             :                 (chan->global_identifier), chan, n_cells);
      83             : 
      84           3 :       flushed = 0;
      85           5 :       while (flushed < n_cells) {
      86           4 :         flushed_this_time =
      87           8 :           channel_flush_some_cells(chan,
      88           4 :                         MIN(MAX_FLUSH_CELLS, (size_t) n_cells - flushed));
      89           4 :         if (flushed_this_time <= 0) break;
      90           2 :         flushed += flushed_this_time;
      91             :       }
      92             : 
      93           3 :       if (flushed < n_cells) {
      94             :         /* We ran out of cells to flush */
      95           2 :         scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
      96             :       } else {
      97             :         /* The channel may still have some cells */
      98           1 :         if (channel_more_to_flush(chan)) {
      99             :         /* The channel goes to either pending or waiting_to_write */
     100           0 :           if (channel_num_cells_writeable(chan) > 0) {
     101             :             /* Add it back to pending later */
     102           0 :             if (!to_readd) to_readd = smartlist_new();
     103           0 :             smartlist_add(to_readd, chan);
     104           0 :             log_debug(LD_SCHED,
     105             :                       "Channel %"PRIu64 " at %p "
     106             :                       "is still pending",
     107             :                       (chan->global_identifier),
     108             :                       chan);
     109             :           } else {
     110             :             /* It's waiting to be able to write more */
     111           0 :             scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
     112             :           }
     113             :         } else {
     114             :           /* No cells left; it can go to idle or waiting_for_cells */
     115           1 :           if (channel_num_cells_writeable(chan) > 0) {
     116             :             /*
     117             :              * It can still accept writes, so it goes to
     118             :              * waiting_for_cells
     119             :              */
     120           1 :             scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
     121             :           } else {
     122             :             /*
     123             :              * We exactly filled up the output queue with all available
     124             :              * cells; go to idle.
     125             :              */
     126           0 :             scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
     127             :           }
     128             :         }
     129             :       }
     130             : 
     131           3 :       log_debug(LD_SCHED,
     132             :                 "Scheduler flushed %d cells onto pending channel "
     133             :                 "%"PRIu64 " at %p",
     134             :                 (int)flushed, (chan->global_identifier),
     135             :                 chan);
     136             :     } else {
     137           1 :       log_info(LD_SCHED,
     138             :                "Scheduler saw pending channel %"PRIu64 " at %p with "
     139             :                "no cells writeable",
     140             :                (chan->global_identifier), chan);
     141             :       /* Put it back to WAITING_TO_WRITE */
     142           1 :       scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
     143             :     }
     144             :   }
     145             : 
     146             :   /* Readd any channels we need to */
     147           2 :   if (to_readd) {
     148           0 :     SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
     149           0 :       scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING);
     150           0 :       smartlist_pqueue_add(cp,
     151             :                            scheduler_compare_channels,
     152             :                            offsetof(channel_t, sched_heap_idx),
     153             :                            readd_chan);
     154           0 :     } SMARTLIST_FOREACH_END(readd_chan);
     155           0 :     smartlist_free(to_readd);
     156             :   }
     157             : 
     158           2 :   n_chans_after = smartlist_len(cp);
     159           2 :   log_debug(LD_SCHED, "Scheduler handled %d of %d pending channels",
     160             :             n_chans_before - n_chans_after, n_chans_before);
     161           2 : }
     162             : 
     163             : /* Stores the vanilla scheduler function pointers. */
     164             : static scheduler_t vanilla_scheduler = {
     165             :   .type = SCHEDULER_VANILLA,
     166             :   .free_all = NULL,
     167             :   .on_channel_free = NULL,
     168             :   .init = NULL,
     169             :   .on_new_consensus = NULL,
     170             :   .schedule = vanilla_scheduler_schedule,
     171             :   .run = vanilla_scheduler_run,
     172             :   .on_new_options = NULL,
     173             : };
     174             : 
     175             : scheduler_t *
     176          35 : get_vanilla_scheduler(void)
     177             : {
     178          35 :   return &vanilla_scheduler;
     179             : }

Generated by: LCOV version 1.14