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