/** * Check whether we're seen as coordinator by all the nodes in our clique. * NOTE: This function assumes that we see ourself as coord. * * \return true if the whole clique agrees that we are coord, false otherwise */ static bool clique_sees_self_as_coord(void) { exa_nodeid_t node_id; for (node_id = 0; node_id < self->view.num_seen; node_id++) if (exa_nodeset_contains(&self->view.clique, node_id)) { sup_node_t *node = sup_cluster_node(&cluster, node_id); EXA_ASSERT(node); if (!self_sees(node)) return false; if (!exa_nodeset_equals(&node->view.clique, &self->view.clique)) return false; if (node->view.coord != self->view.coord) return false; } __trace("clique_sees_self_as_coord -> YES"); return true; }
/** * Check equality of two views. * * Two views are equal if both their nodes seen and * their coordinators are equal. * * \param[in] v1 View * \param[in] v2 View * * \return true if views are equal, false otherwise */ bool sup_view_equals(const sup_view_t *v1, const sup_view_t *v2) { EXA_ASSERT(v1 && v2); return (v1->coord == v2->coord && exa_nodeset_equals(&v1->nodes_seen, &v2->nodes_seen)); }
/** * Called in state ACCEPT to process a ping. * * \param[in] ping Ping to process */ static void state_accept_process_ping(const sup_ping_t *ping) { switch (ping->view.state) { case SUP_STATE_UNKNOWN: /* Not possible */ break; case SUP_STATE_CHANGE: /* Ignore: the event that triggered the sender's state change will * eventually be seen by us, and our view will change too */ break; case SUP_STATE_ACCEPT: if (self_is_coord()) { /* If the sender sees ourself as its coordinator and has accepted * our proposed clique, check if we have reached the point where all * nodes have accepted. * * If so, commit the clique. This will eventually trigger the commit * on all other nodes. */ if (ping->view.coord == self->id && ping->view.accepted == self->view.accepted) { if (clique_has_accepted(self->view.accepted)) { __trace(":):) ALL IN CLIQUE HAVE ACCEPTED VIEW WITH GEN %u", self->view.accepted); commit_clique(); } } } break; case SUP_STATE_COMMIT: if (!self_is_coord()) { /* If the sender views the same clique as us and its committed * generation is equal to our accepted generation, commit. */ if (exa_nodeset_equals(&ping->view.clique, &accepted_clique) && ping->view.committed == self->view.accepted && ping->view.committed > self->view.committed) { __trace("saw commit %u from %u and i accepted %u => i commit %u", ping->view.committed, ping->sender, self->view.accepted, self->view.accepted); commit_clique(); } } break; } }
/** * Before sending a ping. */ static void sup_pre_ping(void) { update_last_seen(); #ifdef USE_YAOURT if (yaourt_event_wait(EXAMSG_CSUPD_ID, "sup_pre_ping") != 0) self_view_changed = true; #endif if (self_view_changed || other_view_changed) { exa_nodeset_t new_clique; bool clique_changed; exa_nodeid_t coord; __trace("RECALCULATING CLIQUE"); sup_clique_compute(&cluster, &new_clique); clique_changed = !exa_nodeset_equals(&new_clique, &self->view.clique); if (clique_changed) { exa_nodeset_copy(&self->view.clique, &new_clique); coord = exa_nodeset_first(&self->view.clique); if (coord != self->view.coord) { __trace("new coord: %u", coord); self->view.coord = coord; } } #ifdef DEBUG { char clique_str[EXA_MAX_NODES_NUMBER + 1]; exa_nodeset_to_bin(&self->view.clique, clique_str); __trace("Clique: %s %s", clique_str, clique_changed ? "CHANGED" : ""); } #endif } if (self_view_changed || other_view_changed) { __trace("*** SUP_MSHIP_CHANGE ***"); set_state(SUP_STATE_CHANGE); } #ifdef WITH_TRACE dump_view(0); #endif }
/** * Check whether all nodes in our clique have accepted the clique * (aka membership) whose generation is given. * * \param[in] gen Membership generation * * \return true if all have accepted, false otherwise */ static bool clique_has_accepted(sup_gen_t gen) { exa_nodeid_t node_id; for (node_id = 0; node_id < self->view.num_seen; node_id++) if (exa_nodeset_contains(&self->view.clique, node_id)) { sup_node_t *node = sup_cluster_node(&cluster, node_id); EXA_ASSERT(node); if (!self_sees(node)) return false; if (!exa_nodeset_equals(&node->view.clique, &self->view.clique)) return false; if (node->view.accepted != gen) return false; } return true; }
/** * Called in state CHANGE to process a ping. * * \param[in] ping Ping to process */ static void state_change_process_ping(const sup_ping_t *ping) { switch (ping->view.state) { case SUP_STATE_UNKNOWN: /* Not possible */ break; case SUP_STATE_CHANGE: if (self_is_coord()) { __trace("i'm coord"); /* Go to state ACCEPT if all the nodes in our clique see us as * their coordinator. This will initiate the agreement phase. */ if (clique_sees_self_as_coord()) { __trace("+++ EVERYONE IN MY CLIQUE SEES ME AS COORD"); accept_clique(next_gen()); __trace("\t=> new state=%s, accepted=%u", sup_state_name(self->view.state), self->view.accepted); } } break; case SUP_STATE_ACCEPT: if (!self_is_coord()) { /* Go to state ACCEPT if the sender is our coordinator, views the * same clique as us and its accepted clique generation is higher * than ours. */ if (ping->sender == self->view.coord && exa_nodeset_equals(&ping->view.clique, &self->view.clique) && ping->view.accepted > self_highest_gen()) { __trace("coord ok, view ok, accepted ok => i accept"); accept_clique(ping->view.accepted); } } break; case SUP_STATE_COMMIT: if (!self_is_coord()) { /* Commit the membership if we accepted it */ if (exa_nodeset_equals(&ping->view.clique, &accepted_clique) && ping->view.committed == self->view.accepted && ping->view.committed > self->view.committed) { __trace("saw commit %u from %u and i accepted %u => i commit %u", ping->view.committed, ping->sender, self->view.accepted, self->view.accepted); commit_clique(); } } break; } }