Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include "src/core/client_config/lb_policies/round_robin.h"
35 :
36 : #include <string.h>
37 :
38 : #include <grpc/support/alloc.h>
39 : #include "src/core/transport/connectivity_state.h"
40 :
41 : int grpc_lb_round_robin_trace = 0;
42 :
43 : /** List of entities waiting for a pick.
44 : *
45 : * Once a pick is available, \a target is updated and \a on_complete called. */
46 : typedef struct pending_pick {
47 : struct pending_pick *next;
48 : grpc_pollset *pollset;
49 : grpc_subchannel **target;
50 : grpc_closure *on_complete;
51 : } pending_pick;
52 :
53 : /** List of subchannels in a connectivity READY state */
54 : typedef struct ready_list {
55 : grpc_subchannel *subchannel;
56 : struct ready_list *next;
57 : struct ready_list *prev;
58 : } ready_list;
59 :
60 : typedef struct {
61 : size_t subchannel_idx; /**< Index over p->subchannels */
62 : void *p; /**< round_robin_lb_policy instance */
63 : } connectivity_changed_cb_arg;
64 :
65 : typedef struct {
66 : /** base policy: must be first */
67 : grpc_lb_policy base;
68 :
69 : /** all our subchannels */
70 : grpc_subchannel **subchannels;
71 : size_t num_subchannels;
72 :
73 : /** Callbacks, one per subchannel being watched, to be called when their
74 : * respective connectivity changes */
75 : grpc_closure *connectivity_changed_cbs;
76 : connectivity_changed_cb_arg *cb_args;
77 :
78 : /** mutex protecting remaining members */
79 : gpr_mu mu;
80 : /** have we started picking? */
81 : int started_picking;
82 : /** are we shutting down? */
83 : int shutdown;
84 : /** Connectivity state of the subchannels being watched */
85 : grpc_connectivity_state *subchannel_connectivity;
86 : /** List of picks that are waiting on connectivity */
87 : pending_pick *pending_picks;
88 :
89 : /** our connectivity state tracker */
90 : grpc_connectivity_state_tracker state_tracker;
91 :
92 : /** (Dummy) root of the doubly linked list containing READY subchannels */
93 : ready_list ready_list;
94 : /** Last pick from the ready list. */
95 : ready_list *ready_list_last_pick;
96 :
97 : /** Subchannel index to ready_list node.
98 : *
99 : * Kept in order to remove nodes from the ready list associated with a
100 : * subchannel */
101 : ready_list **subchannel_index_to_readylist_node;
102 : } round_robin_lb_policy;
103 :
104 : /** Returns the next subchannel from the connected list or NULL if the list is
105 : * empty.
106 : *
107 : * Note that this function does *not* advance p->ready_list_last_pick. Use \a
108 : * advance_last_picked_locked() for that. */
109 70 : static ready_list *peek_next_connected_locked(const round_robin_lb_policy *p) {
110 : ready_list *selected;
111 70 : selected = p->ready_list_last_pick->next;
112 :
113 147 : while (selected != NULL) {
114 57 : if (selected == &p->ready_list) {
115 7 : GPR_ASSERT(selected->subchannel == NULL);
116 : /* skip dummy root */
117 7 : selected = selected->next;
118 : } else {
119 50 : GPR_ASSERT(selected->subchannel != NULL);
120 50 : return selected;
121 : }
122 : }
123 20 : return NULL;
124 : }
125 :
126 : /** Advance the \a ready_list picking head. */
127 35 : static void advance_last_picked_locked(round_robin_lb_policy *p) {
128 35 : if (p->ready_list_last_pick->next != NULL) { /* non-empty list */
129 35 : p->ready_list_last_pick = p->ready_list_last_pick->next;
130 35 : if (p->ready_list_last_pick == &p->ready_list) {
131 : /* skip dummy root */
132 7 : p->ready_list_last_pick = p->ready_list_last_pick->next;
133 : }
134 : } else { /* should be an empty list */
135 0 : GPR_ASSERT(p->ready_list_last_pick == &p->ready_list);
136 : }
137 :
138 35 : if (grpc_lb_round_robin_trace) {
139 0 : gpr_log(GPR_DEBUG, "[READYLIST] ADVANCED LAST PICK. NOW AT NODE %p (SC %p)",
140 0 : p->ready_list_last_pick, p->ready_list_last_pick->subchannel);
141 : }
142 35 : }
143 :
144 : /** Prepends (relative to the root at p->ready_list) the connected subchannel \a
145 : * csc to the list of ready subchannels. */
146 20 : static ready_list *add_connected_sc_locked(round_robin_lb_policy *p,
147 : grpc_subchannel *csc) {
148 20 : ready_list *new_elem = gpr_malloc(sizeof(ready_list));
149 20 : new_elem->subchannel = csc;
150 20 : if (p->ready_list.prev == NULL) {
151 : /* first element */
152 5 : new_elem->next = &p->ready_list;
153 5 : new_elem->prev = &p->ready_list;
154 5 : p->ready_list.next = new_elem;
155 5 : p->ready_list.prev = new_elem;
156 : } else {
157 15 : new_elem->next = &p->ready_list;
158 15 : new_elem->prev = p->ready_list.prev;
159 15 : p->ready_list.prev->next = new_elem;
160 15 : p->ready_list.prev = new_elem;
161 : }
162 20 : if (grpc_lb_round_robin_trace) {
163 0 : gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, csc);
164 : }
165 20 : return new_elem;
166 : }
167 :
168 : /** Removes \a node from the list of connected subchannels */
169 10 : static void remove_disconnected_sc_locked(round_robin_lb_policy *p,
170 : ready_list *node) {
171 10 : if (node == NULL) {
172 10 : return;
173 : }
174 10 : if (node == p->ready_list_last_pick) {
175 : /* If removing the lastly picked node, reset the last pick pointer to the
176 : * dummy root of the list */
177 3 : p->ready_list_last_pick = &p->ready_list;
178 : }
179 :
180 : /* removing last item */
181 10 : if (node->next == &p->ready_list && node->prev == &p->ready_list) {
182 2 : GPR_ASSERT(p->ready_list.next == node);
183 2 : GPR_ASSERT(p->ready_list.prev == node);
184 2 : p->ready_list.next = NULL;
185 2 : p->ready_list.prev = NULL;
186 : } else {
187 8 : node->prev->next = node->next;
188 8 : node->next->prev = node->prev;
189 : }
190 :
191 10 : if (grpc_lb_round_robin_trace) {
192 0 : gpr_log(GPR_DEBUG, "[READYLIST] REMOVED NODE %p (SC %p)", node,
193 : node->subchannel);
194 : }
195 :
196 10 : node->next = NULL;
197 10 : node->prev = NULL;
198 10 : node->subchannel = NULL;
199 :
200 10 : gpr_free(node);
201 : }
202 :
203 104 : static void del_interested_parties_locked(grpc_exec_ctx *exec_ctx,
204 : round_robin_lb_policy *p,
205 : const size_t subchannel_idx) {
206 : pending_pick *pp;
207 272 : for (pp = p->pending_picks; pp; pp = pp->next) {
208 336 : grpc_subchannel_del_interested_party(
209 168 : exec_ctx, p->subchannels[subchannel_idx], pp->pollset);
210 : }
211 104 : }
212 :
213 5 : void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
214 5 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
215 : size_t i;
216 : ready_list *elem;
217 25 : for (i = 0; i < p->num_subchannels; i++) {
218 20 : del_interested_parties_locked(exec_ctx, p, i);
219 : }
220 25 : for (i = 0; i < p->num_subchannels; i++) {
221 20 : GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "round_robin");
222 : }
223 5 : gpr_free(p->connectivity_changed_cbs);
224 5 : gpr_free(p->subchannel_connectivity);
225 :
226 5 : grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
227 5 : gpr_free(p->subchannels);
228 5 : gpr_mu_destroy(&p->mu);
229 :
230 5 : elem = p->ready_list.next;
231 20 : while (elem != NULL && elem != &p->ready_list) {
232 : ready_list *tmp;
233 10 : tmp = elem->next;
234 10 : elem->next = NULL;
235 10 : elem->prev = NULL;
236 10 : elem->subchannel = NULL;
237 10 : gpr_free(elem);
238 10 : elem = tmp;
239 : }
240 5 : gpr_free(p->subchannel_index_to_readylist_node);
241 5 : gpr_free(p->cb_args);
242 5 : gpr_free(p);
243 5 : }
244 :
245 5 : void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
246 : size_t i;
247 5 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
248 : pending_pick *pp;
249 5 : gpr_mu_lock(&p->mu);
250 :
251 25 : for (i = 0; i < p->num_subchannels; i++) {
252 20 : del_interested_parties_locked(exec_ctx, p, i);
253 : }
254 :
255 5 : p->shutdown = 1;
256 24 : while ((pp = p->pending_picks)) {
257 14 : p->pending_picks = pp->next;
258 14 : *pp->target = NULL;
259 14 : grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 0);
260 14 : gpr_free(pp);
261 : }
262 5 : grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
263 : GRPC_CHANNEL_FATAL_FAILURE, "shutdown");
264 5 : gpr_mu_unlock(&p->mu);
265 5 : }
266 :
267 5 : static void start_picking(grpc_exec_ctx *exec_ctx, round_robin_lb_policy *p) {
268 : size_t i;
269 5 : p->started_picking = 1;
270 :
271 25 : for (i = 0; i < p->num_subchannels; i++) {
272 20 : p->subchannel_connectivity[i] = GRPC_CHANNEL_IDLE;
273 40 : grpc_subchannel_notify_on_state_change(exec_ctx, p->subchannels[i],
274 20 : &p->subchannel_connectivity[i],
275 20 : &p->connectivity_changed_cbs[i]);
276 20 : GRPC_LB_POLICY_REF(&p->base, "round_robin_connectivity");
277 : }
278 5 : }
279 :
280 0 : void rr_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
281 0 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
282 0 : gpr_mu_lock(&p->mu);
283 0 : if (!p->started_picking) {
284 0 : start_picking(exec_ctx, p);
285 : }
286 0 : gpr_mu_unlock(&p->mu);
287 0 : }
288 :
289 50 : void rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
290 : grpc_pollset *pollset, grpc_metadata_batch *initial_metadata,
291 : grpc_subchannel **target, grpc_closure *on_complete) {
292 : size_t i;
293 50 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
294 : pending_pick *pp;
295 : ready_list *selected;
296 50 : gpr_mu_lock(&p->mu);
297 50 : if ((selected = peek_next_connected_locked(p))) {
298 30 : gpr_mu_unlock(&p->mu);
299 30 : *target = selected->subchannel;
300 30 : if (grpc_lb_round_robin_trace) {
301 0 : gpr_log(GPR_DEBUG, "[RR PICK] TARGET <-- SUBCHANNEL %p (NODE %p)",
302 : selected->subchannel, selected);
303 : }
304 : /* only advance the last picked pointer if the selection was used */
305 30 : advance_last_picked_locked(p);
306 30 : on_complete->cb(exec_ctx, on_complete->cb_arg, 1);
307 : } else {
308 20 : if (!p->started_picking) {
309 5 : start_picking(exec_ctx, p);
310 : }
311 100 : for (i = 0; i < p->num_subchannels; i++) {
312 80 : grpc_subchannel_add_interested_party(exec_ctx, p->subchannels[i],
313 : pollset);
314 : }
315 20 : pp = gpr_malloc(sizeof(*pp));
316 20 : pp->next = p->pending_picks;
317 20 : pp->pollset = pollset;
318 20 : pp->target = target;
319 20 : pp->on_complete = on_complete;
320 20 : p->pending_picks = pp;
321 20 : gpr_mu_unlock(&p->mu);
322 : }
323 50 : }
324 :
325 188 : static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
326 : int iomgr_success) {
327 188 : connectivity_changed_cb_arg *cb_arg = arg;
328 188 : round_robin_lb_policy *p = cb_arg->p;
329 : /* index over p->subchannels of this cb's subchannel */
330 188 : const size_t this_idx = cb_arg->subchannel_idx;
331 : pending_pick *pp;
332 : ready_list *selected;
333 :
334 188 : int unref = 0;
335 :
336 : /* connectivity state of this cb's subchannel */
337 : grpc_connectivity_state *this_connectivity;
338 :
339 188 : gpr_mu_lock(&p->mu);
340 :
341 188 : this_connectivity = &p->subchannel_connectivity[this_idx];
342 :
343 188 : if (p->shutdown) {
344 20 : unref = 1;
345 : } else {
346 168 : switch (*this_connectivity) {
347 : case GRPC_CHANNEL_READY:
348 20 : grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
349 : GRPC_CHANNEL_READY, "connecting_ready");
350 : /* add the newly connected subchannel to the list of connected ones.
351 : * Note that it goes to the "end of the line". */
352 40 : p->subchannel_index_to_readylist_node[this_idx] =
353 20 : add_connected_sc_locked(p, p->subchannels[this_idx]);
354 : /* at this point we know there's at least one suitable subchannel. Go
355 : * ahead and pick one and notify the pending suitors in
356 : * p->pending_picks. This preemtively replicates rr_pick()'s actions. */
357 20 : selected = peek_next_connected_locked(p);
358 20 : if (p->pending_picks != NULL) {
359 : /* if the selected subchannel is going to be used for the pending
360 : * picks, update the last picked pointer */
361 5 : advance_last_picked_locked(p);
362 : }
363 46 : while ((pp = p->pending_picks)) {
364 6 : p->pending_picks = pp->next;
365 6 : *pp->target = selected->subchannel;
366 6 : if (grpc_lb_round_robin_trace) {
367 0 : gpr_log(GPR_DEBUG,
368 : "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)",
369 : selected->subchannel, selected);
370 : }
371 6 : grpc_subchannel_del_interested_party(exec_ctx, selected->subchannel,
372 : pp->pollset);
373 6 : grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1);
374 6 : gpr_free(pp);
375 : }
376 40 : grpc_subchannel_notify_on_state_change(
377 20 : exec_ctx, p->subchannels[this_idx], this_connectivity,
378 20 : &p->connectivity_changed_cbs[this_idx]);
379 20 : break;
380 : case GRPC_CHANNEL_CONNECTING:
381 : case GRPC_CHANNEL_IDLE:
382 84 : grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
383 : *this_connectivity, "connecting_changed");
384 168 : grpc_subchannel_notify_on_state_change(
385 84 : exec_ctx, p->subchannels[this_idx], this_connectivity,
386 84 : &p->connectivity_changed_cbs[this_idx]);
387 84 : break;
388 : case GRPC_CHANNEL_TRANSIENT_FAILURE:
389 64 : del_interested_parties_locked(exec_ctx, p, this_idx);
390 : /* renew state notification */
391 128 : grpc_subchannel_notify_on_state_change(
392 64 : exec_ctx, p->subchannels[this_idx], this_connectivity,
393 64 : &p->connectivity_changed_cbs[this_idx]);
394 :
395 : /* remove from ready list if still present */
396 64 : if (p->subchannel_index_to_readylist_node[this_idx] != NULL) {
397 10 : remove_disconnected_sc_locked(
398 10 : p, p->subchannel_index_to_readylist_node[this_idx]);
399 10 : p->subchannel_index_to_readylist_node[this_idx] = NULL;
400 : }
401 64 : grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
402 : GRPC_CHANNEL_TRANSIENT_FAILURE,
403 : "connecting_transient_failure");
404 64 : break;
405 : case GRPC_CHANNEL_FATAL_FAILURE:
406 0 : del_interested_parties_locked(exec_ctx, p, this_idx);
407 0 : if (p->subchannel_index_to_readylist_node[this_idx] != NULL) {
408 0 : remove_disconnected_sc_locked(
409 0 : p, p->subchannel_index_to_readylist_node[this_idx]);
410 0 : p->subchannel_index_to_readylist_node[this_idx] = NULL;
411 : }
412 :
413 0 : GPR_SWAP(grpc_subchannel *, p->subchannels[this_idx],
414 : p->subchannels[p->num_subchannels - 1]);
415 0 : p->num_subchannels--;
416 0 : GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels],
417 : "round_robin");
418 :
419 0 : if (p->num_subchannels == 0) {
420 0 : grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
421 : GRPC_CHANNEL_FATAL_FAILURE,
422 : "no_more_channels");
423 0 : while ((pp = p->pending_picks)) {
424 0 : p->pending_picks = pp->next;
425 0 : *pp->target = NULL;
426 0 : grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1);
427 0 : gpr_free(pp);
428 : }
429 0 : unref = 1;
430 : } else {
431 0 : grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
432 : GRPC_CHANNEL_TRANSIENT_FAILURE,
433 : "subchannel_failed");
434 : }
435 : } /* switch */
436 : } /* !unref */
437 :
438 188 : gpr_mu_unlock(&p->mu);
439 :
440 188 : if (unref) {
441 20 : GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "round_robin_connectivity");
442 : }
443 188 : }
444 :
445 5 : static void rr_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
446 : grpc_transport_op *op) {
447 5 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
448 : size_t i;
449 : size_t n;
450 : grpc_subchannel **subchannels;
451 :
452 5 : gpr_mu_lock(&p->mu);
453 5 : n = p->num_subchannels;
454 5 : subchannels = gpr_malloc(n * sizeof(*subchannels));
455 25 : for (i = 0; i < n; i++) {
456 20 : subchannels[i] = p->subchannels[i];
457 20 : GRPC_SUBCHANNEL_REF(subchannels[i], "rr_broadcast");
458 : }
459 5 : gpr_mu_unlock(&p->mu);
460 :
461 25 : for (i = 0; i < n; i++) {
462 20 : grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op);
463 20 : GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "rr_broadcast");
464 : }
465 5 : gpr_free(subchannels);
466 5 : }
467 :
468 5 : static grpc_connectivity_state rr_check_connectivity(grpc_exec_ctx *exec_ctx,
469 : grpc_lb_policy *pol) {
470 5 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
471 : grpc_connectivity_state st;
472 5 : gpr_mu_lock(&p->mu);
473 5 : st = grpc_connectivity_state_check(&p->state_tracker);
474 5 : gpr_mu_unlock(&p->mu);
475 5 : return st;
476 : }
477 :
478 59 : static void rr_notify_on_state_change(grpc_exec_ctx *exec_ctx,
479 : grpc_lb_policy *pol,
480 : grpc_connectivity_state *current,
481 : grpc_closure *notify) {
482 59 : round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
483 59 : gpr_mu_lock(&p->mu);
484 59 : grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker,
485 : current, notify);
486 59 : gpr_mu_unlock(&p->mu);
487 59 : }
488 :
489 : static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = {
490 : rr_destroy, rr_shutdown, rr_pick, rr_exit_idle, rr_broadcast,
491 : rr_check_connectivity, rr_notify_on_state_change};
492 :
493 2501 : static void round_robin_factory_ref(grpc_lb_policy_factory *factory) {}
494 :
495 0 : static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {}
496 :
497 5 : static grpc_lb_policy *create_round_robin(grpc_lb_policy_factory *factory,
498 : grpc_lb_policy_args *args) {
499 : size_t i;
500 5 : round_robin_lb_policy *p = gpr_malloc(sizeof(*p));
501 5 : GPR_ASSERT(args->num_subchannels > 0);
502 5 : memset(p, 0, sizeof(*p));
503 5 : grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable);
504 5 : p->subchannels =
505 5 : gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels);
506 5 : p->num_subchannels = args->num_subchannels;
507 5 : grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE,
508 : "round_robin");
509 5 : memcpy(p->subchannels, args->subchannels,
510 5 : sizeof(grpc_subchannel *) * args->num_subchannels);
511 :
512 5 : gpr_mu_init(&p->mu);
513 5 : p->connectivity_changed_cbs =
514 5 : gpr_malloc(sizeof(grpc_closure) * args->num_subchannels);
515 5 : p->subchannel_connectivity =
516 5 : gpr_malloc(sizeof(grpc_connectivity_state) * args->num_subchannels);
517 :
518 5 : p->cb_args =
519 5 : gpr_malloc(sizeof(connectivity_changed_cb_arg) * args->num_subchannels);
520 25 : for (i = 0; i < args->num_subchannels; i++) {
521 20 : p->cb_args[i].subchannel_idx = i;
522 20 : p->cb_args[i].p = p;
523 20 : grpc_closure_init(&p->connectivity_changed_cbs[i], rr_connectivity_changed,
524 20 : &p->cb_args[i]);
525 : }
526 :
527 : /* The (dummy node) root of the ready list */
528 5 : p->ready_list.subchannel = NULL;
529 5 : p->ready_list.prev = NULL;
530 5 : p->ready_list.next = NULL;
531 5 : p->ready_list_last_pick = &p->ready_list;
532 :
533 5 : p->subchannel_index_to_readylist_node =
534 5 : gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels);
535 5 : memset(p->subchannel_index_to_readylist_node, 0,
536 5 : sizeof(grpc_subchannel *) * args->num_subchannels);
537 5 : return &p->base;
538 : }
539 :
540 : static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = {
541 : round_robin_factory_ref, round_robin_factory_unref, create_round_robin,
542 : "round_robin"};
543 :
544 : static grpc_lb_policy_factory round_robin_lb_policy_factory = {
545 : &round_robin_factory_vtable};
546 :
547 2501 : grpc_lb_policy_factory *grpc_round_robin_lb_factory_create() {
548 2501 : return &round_robin_lb_policy_factory;
549 : }
|