libuav original

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers persistent_state.hpp Source File

persistent_state.hpp

00001 /*
00002  * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #ifndef UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_DISTRIBUTED_PERSISTENT_STATE_HPP_INCLUDED
00006 #define UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_DISTRIBUTED_PERSISTENT_STATE_HPP_INCLUDED
00007 
00008 #include <uavcan/build_config.hpp>
00009 #include <uavcan/debug.hpp>
00010 #include <uavcan/protocol/dynamic_node_id_server/distributed/types.hpp>
00011 #include <uavcan/protocol/dynamic_node_id_server/distributed/log.hpp>
00012 #include <uavcan/protocol/dynamic_node_id_server/storage_marshaller.hpp>
00013 #include <uavcan/protocol/dynamic_node_id_server/event.hpp>
00014 
00015 namespace uavcan
00016 {
00017 namespace dynamic_node_id_server
00018 {
00019 namespace distributed
00020 {
00021 /**
00022  * This class is a convenient container for persistent state variables defined by Raft.
00023  * Writes are slow, reads are instantaneous.
00024  */
00025 class PersistentState
00026 {
00027     IStorageBackend& storage_;
00028     IEventTracer& tracer_;
00029 
00030     Term current_term_;
00031     NodeID voted_for_;
00032     Log log_;
00033 
00034     static IStorageBackend::String getCurrentTermKey() { return "current_term"; }
00035     static IStorageBackend::String getVotedForKey() { return "voted_for"; }
00036 
00037 public:
00038     PersistentState(IStorageBackend& storage, IEventTracer& tracer)
00039         : storage_(storage)
00040         , tracer_(tracer)
00041         , current_term_(0)
00042         , log_(storage, tracer)
00043     { }
00044 
00045     /**
00046      * Initialization is performed as follows (every step may fail and return an error):
00047      *  1. Log is restored or initialized.
00048      *  2. Current term is restored. If there was no current term stored and the log is empty, it will be initialized
00049      *     with zero.
00050      *  3. VotedFor value is restored. If there was no VotedFor value stored, the log is empty, and the current term is
00051      *     zero, the value will be initialized with zero.
00052      */
00053     int init()
00054     {
00055         /*
00056          * Reading log
00057          */
00058         int res = log_.init();
00059         if (res < 0)
00060         {
00061             UAVCAN_TRACE("dynamic_node_id_server::distributed::PersistentState", "Log init failed: %d", res);
00062             return res;
00063         }
00064 
00065         const Entry* const last_entry = log_.getEntryAtIndex(log_.getLastIndex());
00066         if (last_entry == UAVCAN_NULLPTR)
00067         {
00068             UAVCAN_ASSERT(0);
00069             return -ErrLogic;
00070         }
00071 
00072         const bool log_is_empty = (log_.getLastIndex() == 0) && (last_entry->term == 0);
00073 
00074         StorageMarshaller io(storage_);
00075 
00076         /*
00077          * Reading currentTerm
00078          */
00079         if (storage_.get(getCurrentTermKey()).empty() && log_is_empty)
00080         {
00081             // First initialization
00082             current_term_ = 0;
00083             res = io.setAndGetBack(getCurrentTermKey(), current_term_);
00084             if (res < 0)
00085             {
00086                 UAVCAN_TRACE("dynamic_node_id_server::distributed::PersistentState",
00087                              "Failed to init current term: %d", res);
00088                 return res;
00089             }
00090             if (current_term_ != 0)
00091             {
00092                 return -ErrFailure;
00093             }
00094         }
00095         else
00096         {
00097             // Restoring
00098             res = io.get(getCurrentTermKey(), current_term_);
00099             if (res < 0)
00100             {
00101                 UAVCAN_TRACE("dynamic_node_id_server::distributed::PersistentState",
00102                              "Failed to read current term: %d", res);
00103                 return res;
00104             }
00105         }
00106 
00107         tracer_.onEvent(TraceRaftCurrentTermRestored, current_term_);
00108 
00109         if (current_term_ < last_entry->term)
00110         {
00111             UAVCAN_TRACE("dynamic_node_id_server::distributed::PersistentState",
00112                          "Persistent storage is damaged: current term is less than term of the last log entry (%u < %u)",
00113                          unsigned(current_term_), unsigned(last_entry->term));
00114             return -ErrLogic;
00115         }
00116 
00117         /*
00118          * Reading votedFor
00119          */
00120         if (storage_.get(getVotedForKey()).empty() && log_is_empty && (current_term_ == 0))
00121         {
00122             // First initialization
00123             voted_for_ = NodeID(0);
00124             uint32_t stored_voted_for = 0;
00125             res = io.setAndGetBack(getVotedForKey(), stored_voted_for);
00126             if (res < 0)
00127             {
00128                 UAVCAN_TRACE("dynamic_node_id_server::distributed::PersistentState",
00129                              "Failed to init votedFor: %d", res);
00130                 return res;
00131             }
00132             if (stored_voted_for != 0)
00133             {
00134                 return -ErrFailure;
00135             }
00136         }
00137         else
00138         {
00139             // Restoring
00140             uint32_t stored_voted_for = 0;
00141             res = io.get(getVotedForKey(), stored_voted_for);
00142             if (res < 0)
00143             {
00144                 UAVCAN_TRACE("dynamic_node_id_server::distributed::PersistentState",
00145                              "Failed to read votedFor: %d", res);
00146                 return res;
00147             }
00148             if (stored_voted_for > NodeID::Max)
00149             {
00150                 return -ErrInvalidConfiguration;
00151             }
00152             voted_for_ = NodeID(uint8_t(stored_voted_for));
00153         }
00154 
00155         tracer_.onEvent(TraceRaftVotedForRestored, voted_for_.get());
00156 
00157         return 0;
00158     }
00159 
00160     Term getCurrentTerm() const { return current_term_; }
00161 
00162     NodeID getVotedFor() const { return voted_for_; }
00163     bool isVotedForSet() const { return voted_for_.isUnicast(); }
00164 
00165     Log& getLog()             { return log_; }
00166     const Log& getLog() const { return log_; }
00167 
00168     /**
00169      * Invokes storage IO.
00170      */
00171     int setCurrentTerm(Term term)
00172     {
00173         if (term < current_term_)
00174         {
00175             UAVCAN_ASSERT(0);
00176             return -ErrInvalidParam;
00177         }
00178 
00179         tracer_.onEvent(TraceRaftCurrentTermUpdate, term);
00180 
00181         StorageMarshaller io(storage_);
00182 
00183         Term tmp = term;
00184         int res = io.setAndGetBack(getCurrentTermKey(), tmp);
00185         if (res < 0)
00186         {
00187             return res;
00188         }
00189 
00190         if (tmp != term)
00191         {
00192             return -ErrFailure;
00193         }
00194 
00195         current_term_ = term;
00196         return 0;
00197     }
00198 
00199     /**
00200      * Invokes storage IO.
00201      */
00202     int setVotedFor(NodeID node_id)
00203     {
00204         if (!node_id.isValid())
00205         {
00206             UAVCAN_ASSERT(0);
00207             return -ErrInvalidParam;
00208         }
00209 
00210         tracer_.onEvent(TraceRaftVotedForUpdate, node_id.get());
00211 
00212         StorageMarshaller io(storage_);
00213 
00214         uint32_t tmp = node_id.get();
00215         int res = io.setAndGetBack(getVotedForKey(), tmp);
00216         if (res < 0)
00217         {
00218             return res;
00219         }
00220 
00221         if (node_id.get() != tmp)
00222         {
00223             return -ErrFailure;
00224         }
00225 
00226         voted_for_ = node_id;
00227         return 0;
00228     }
00229 
00230     int resetVotedFor() { return setVotedFor(NodeID(0)); }
00231 };
00232 
00233 }
00234 }
00235 }
00236 
00237 #endif // Include guard