LCOV - code coverage report
Current view: top level - src/core/client_config/lb_policies - round_robin.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 226 258 87.6 %
Date: 2015-10-10 Functions: 16 18 88.9 %

          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             : }

Generated by: LCOV version 1.10